summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2017-12-06 13:18:16 +0100
committerNikos Mavrogiannopoulos <nmav@redhat.com>2018-02-19 15:29:36 +0100
commit9829ef9a3ca06d60472599df7c74ebb9a53f1fe2 (patch)
tree4b238387df98a69d5c9dfb7617f8203d139084c6
parent74b73eab2460119dd7d5a3add2a1425c462f54b2 (diff)
downloadgnutls-9829ef9a3ca06d60472599df7c74ebb9a53f1fe2.tar.gz
introduced gnutls_certificate_retrieve_function3
That allows a certificate callback to provide OCSP responses in addition to certificates. That also introduces a flags option which currently accepts GNUTLS_CERT_RETR_DEINIT_ALL which allows the callback to specify whether the provided data should be deinitialized. To simplify the certificate callback code, all previous (now legacy) callbacks are implemented as wrappers over the new callback function. Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r--lib/auth/cert.c254
-rw-r--r--lib/auth/cert.h5
-rw-r--r--lib/cert.c284
-rw-r--r--lib/ext/post_handshake.c2
-rw-r--r--lib/gnutls_int.h10
-rw-r--r--lib/includes/gnutls/abstract.h33
-rw-r--r--lib/libgnutls.map1
7 files changed, 366 insertions, 223 deletions
diff --git a/lib/auth/cert.c b/lib/auth/cert.c
index 2d02465089..085857bcd2 100644
--- a/lib/auth/cert.c
+++ b/lib/auth/cert.c
@@ -47,19 +47,10 @@
#include "abstract_int.h"
#include "debug.h"
-static gnutls_pcert_st *alloc_and_load_x509_certs(gnutls_x509_crt_t *
- certs, unsigned);
-static gnutls_privkey_t alloc_and_load_x509_key(gnutls_x509_privkey_t key,
- int deinit);
-
-#ifdef ENABLE_PKCS11
-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_datum_t *ocsp, int nocsp,
gnutls_privkey_t key, int need_free,
gnutls_status_request_ocsp_func ocsp_func,
void *ocsp_func_ptr);
@@ -356,14 +347,13 @@ call_get_cert_callback(gnutls_session_t session,
int issuers_dn_length,
gnutls_pk_algorithm_t * pk_algos, int pk_algos_length)
{
- unsigned i;
- gnutls_pcert_st *local_certs = NULL;
gnutls_privkey_t local_key = NULL;
int ret = GNUTLS_E_INTERNAL_ERROR;
gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
gnutls_certificate_credentials_t cred;
- gnutls_retr2_st st2;
gnutls_pcert_st *pcert = NULL;
+ gnutls_datum_t *ocsp = NULL;
+ unsigned int ocsp_length = 0;
unsigned int pcert_length = 0;
cred = (gnutls_certificate_credentials_t)
@@ -373,15 +363,23 @@ call_get_cert_callback(gnutls_session_t session,
return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
}
- memset(&st2, 0, sizeof(st2));
+ if (cred->get_cert_callback3) {
+ struct gnutls_cert_retr_st info;
+ unsigned int flags = 0;
+
+ memset(&info, 0, sizeof(info));
+ info.req_ca_rdn = issuers_dn;
+ info.nreqs = issuers_dn_length;
+ info.pk_algos = pk_algos;
+ info.pk_algos_length = pk_algos_length;
+ info.cred = cred;
- if (cred->get_cert_callback2) {
/* we avoid all allocations and transformations */
ret =
- cred->get_cert_callback2(session, issuers_dn,
- issuers_dn_length, pk_algos,
- pk_algos_length, &pcert,
- &pcert_length, &local_key);
+ cred->get_cert_callback3(session, &info,
+ &pcert, &pcert_length,
+ &ocsp, &ocsp_length,
+ &local_key, &flags);
if (ret < 0)
return gnutls_assert_val(GNUTLS_E_USER_ERROR);
@@ -394,100 +392,15 @@ call_get_cert_callback(gnutls_session_t session,
}
_gnutls_selected_certs_set(session, pcert, pcert_length,
- local_key, 0, NULL, NULL);
+ ocsp, ocsp_length,
+ local_key, flags&GNUTLS_CERT_RETR_DEINIT_ALL?1:0,
+ NULL, NULL);
return 0;
-
- } else if (cred->get_cert_callback) {
- ret =
- cred->get_cert_callback(session, issuers_dn,
- issuers_dn_length, pk_algos,
- pk_algos_length, &st2);
-
- } else { /* compatibility mode */
+ } else {
gnutls_assert();
return GNUTLS_E_INTERNAL_ERROR;
}
-
- if (ret < 0) {
- gnutls_assert();
- return GNUTLS_E_USER_ERROR;
- }
-
- if (st2.ncerts == 0)
- return 0; /* no certificate was selected */
-
- if (type != st2.cert_type) {
- gnutls_assert();
- ret = GNUTLS_E_INVALID_REQUEST;
- goto cleanup;
- }
-
- if (type == GNUTLS_CRT_X509) {
- local_certs =
- alloc_and_load_x509_certs(st2.cert.x509, st2.ncerts);
- } else { /* PGP */
- ret = GNUTLS_E_UNIMPLEMENTED_FEATURE;
- goto cleanup;
- }
-
- if (local_certs == NULL) {
- gnutls_assert();
- ret = GNUTLS_E_MEMORY_ERROR;
- goto cleanup;
- }
-
- switch (st2.key_type) {
- case GNUTLS_PRIVKEY_PKCS11:
-#ifdef ENABLE_PKCS11
- if (st2.key.pkcs11 != NULL) {
- local_key =
- alloc_and_load_pkcs11_key(st2.key.pkcs11,
- st2.deinit_all);
- if (local_key == NULL) {
- gnutls_assert();
- ret = GNUTLS_E_INTERNAL_ERROR;
- goto cleanup;
- }
- }
-#endif
- break;
- case GNUTLS_PRIVKEY_X509:
- if (st2.key.x509 != NULL) {
- local_key =
- alloc_and_load_x509_key(st2.key.x509,
- st2.deinit_all);
- if (local_key == NULL) {
- gnutls_assert();
- ret = GNUTLS_E_INTERNAL_ERROR;
- goto cleanup;
- }
- }
- break;
- default:
- gnutls_assert();
- ret = GNUTLS_E_INVALID_REQUEST;
- goto cleanup;
- }
-
- _gnutls_selected_certs_set(session, local_certs,
- st2.ncerts, local_key, 1,
- NULL, NULL);
-
- ret = 0;
-
- cleanup:
-
- if (st2.cert_type == GNUTLS_CRT_X509) {
- if (st2.deinit_all) {
- for (i = 0; i < st2.ncerts; i++) {
- gnutls_x509_crt_deinit(st2.cert.x509[i]);
- }
- gnutls_free(st2.cert.x509);
- }
- }
-
- return ret;
}
/* Finds the appropriate certificate depending on the cA Distinguished name
@@ -517,8 +430,7 @@ _gnutls_select_client_cert(gnutls_session_t session,
return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
}
- if (cred->get_cert_callback != NULL
- || cred->get_cert_callback2 != NULL) {
+ if (cred->get_cert_callback3 != NULL) {
/* use a callback to get certificate
*/
@@ -579,11 +491,12 @@ _gnutls_select_client_cert(gnutls_session_t session,
cert_list[0],
cred->certs[indx].
cert_list_length,
+ NULL, 0,
cred->certs[indx].pkey, 0,
NULL, NULL);
} else {
_gnutls_selected_certs_set(session, NULL, 0, NULL, 0,
- NULL, NULL);
+ NULL, 0, NULL, NULL);
}
result = 0;
@@ -1225,109 +1138,6 @@ _gnutls_get_selected_cert(gnutls_session_t session,
return 0;
}
-/* converts the given x509 certificate list to gnutls_pcert_st* and allocates
- * space for them.
- */
-static gnutls_pcert_st *alloc_and_load_x509_certs(gnutls_x509_crt_t *
- certs, unsigned ncerts)
-{
- gnutls_pcert_st *local_certs;
- int ret = 0;
- unsigned i, j;
-
- if (certs == NULL)
- return NULL;
-
- local_certs = gnutls_malloc(sizeof(gnutls_pcert_st) * ncerts);
- if (local_certs == NULL) {
- gnutls_assert();
- return NULL;
- }
-
- for (i = 0; i < ncerts; i++) {
- ret = gnutls_pcert_import_x509(&local_certs[i], certs[i], 0);
- if (ret < 0)
- break;
- }
-
- if (ret < 0) {
- gnutls_assert();
- for (j = 0; j < i; j++) {
- gnutls_pcert_deinit(&local_certs[j]);
- }
- gnutls_free(local_certs);
- return NULL;
- }
-
- return local_certs;
-}
-
-/* converts the given x509 key to gnutls_privkey* and allocates
- * space for it.
- */
-static gnutls_privkey_t
-alloc_and_load_x509_key(gnutls_x509_privkey_t key, int deinit)
-{
- gnutls_privkey_t local_key;
- int ret = 0;
-
- if (key == NULL)
- return NULL;
-
- ret = gnutls_privkey_init(&local_key);
- if (ret < 0) {
- gnutls_assert();
- return NULL;
- }
-
- ret =
- gnutls_privkey_import_x509(local_key, key,
- deinit ?
- GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE : 0);
- if (ret < 0) {
- gnutls_assert();
- gnutls_privkey_deinit(local_key);
- return NULL;
- }
-
- return local_key;
-}
-
-#ifdef ENABLE_PKCS11
-
-/* converts the given raw key to gnutls_privkey* and allocates
- * space for it.
- */
-static gnutls_privkey_t
-alloc_and_load_pkcs11_key(gnutls_pkcs11_privkey_t key, int deinit)
-{
- gnutls_privkey_t local_key;
- int ret = 0;
-
- if (key == NULL)
- return NULL;
-
- ret = gnutls_privkey_init(&local_key);
- if (ret < 0) {
- gnutls_assert();
- return NULL;
- }
-
- ret =
- gnutls_privkey_import_pkcs11(local_key, key,
- deinit ?
- GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE
- : 0);
- if (ret < 0) {
- gnutls_assert();
- gnutls_privkey_deinit(local_key);
- return NULL;
- }
-
- return local_key;
-}
-
-#endif
void _gnutls_selected_certs_deinit(gnutls_session_t session)
{
@@ -1341,6 +1151,13 @@ void _gnutls_selected_certs_deinit(gnutls_session_t session)
}
gnutls_free(session->internals.selected_cert_list);
+ for (i = 0;
+ i < session->internals.selected_ocsp_length; i++) {
+ _gnutls_free_datum(&session->internals.
+ selected_ocsp[i]);
+ }
+ gnutls_free(session->internals.selected_ocsp);
+
gnutls_privkey_deinit(session->internals.selected_key);
}
session->internals.selected_ocsp_func = NULL;
@@ -1356,6 +1173,7 @@ void _gnutls_selected_certs_deinit(gnutls_session_t session)
static void
_gnutls_selected_certs_set(gnutls_session_t session,
gnutls_pcert_st * certs, int ncerts,
+ gnutls_datum_t *ocsp, int nocsp,
gnutls_privkey_t key, int need_free,
gnutls_status_request_ocsp_func ocsp_func,
void *ocsp_func_ptr)
@@ -1364,6 +1182,10 @@ _gnutls_selected_certs_set(gnutls_session_t session,
session->internals.selected_cert_list = certs;
session->internals.selected_cert_list_length = ncerts;
+
+ session->internals.selected_ocsp = ocsp;
+ session->internals.selected_ocsp_length = nocsp;
+
session->internals.selected_key = key;
session->internals.selected_need_free = need_free;
@@ -1486,8 +1308,7 @@ _gnutls_server_select_cert(gnutls_session_t session, const gnutls_cipher_suite_e
/* If the callback which retrieves certificate has been set,
* use it and leave. We make sure that this is called once.
*/
- if (cred->get_cert_callback
- || cred->get_cert_callback2) {
+ if (cred->get_cert_callback3) {
if (session->internals.selected_cert_list_length == 0) {
ret = call_get_cert_callback(session, NULL, 0, NULL, 0);
@@ -1608,6 +1429,7 @@ _gnutls_server_select_cert(gnutls_session_t session, const gnutls_cipher_suite_e
_gnutls_selected_certs_set(session,
&cred->certs[idx].cert_list[0],
cred->certs[idx].cert_list_length,
+ NULL, 0,
cred->certs[idx].pkey, 0,
cred->certs[idx].ocsp_func,
cred->certs[idx].ocsp_func_ptr);
diff --git a/lib/auth/cert.h b/lib/auth/cert.h
index fee7c03ccd..f890f48624 100644
--- a/lib/auth/cert.h
+++ b/lib/auth/cert.h
@@ -77,8 +77,9 @@ typedef struct gnutls_certificate_credentials_st {
/* It's a mess here. However we need to keep the old 3 functions
* for compatibility */
- gnutls_certificate_retrieve_function *get_cert_callback; /* deprecated */
- gnutls_certificate_retrieve_function2 *get_cert_callback2;
+ gnutls_certificate_retrieve_function *legacy_cert_cb1; /* deprecated */
+ gnutls_certificate_retrieve_function2 *legacy_cert_cb2;
+ gnutls_certificate_retrieve_function3 *get_cert_callback3;
gnutls_certificate_verify_function *verify_callback;
diff --git a/lib/cert.c b/lib/cert.c
index 9e3a00f484..617ad8e644 100644
--- a/lib/cert.c
+++ b/lib/cert.c
@@ -236,6 +236,110 @@ gnutls_certificate_allocate_credentials(gnutls_certificate_credentials_t *
return 0;
}
+/* converts the given x509 certificate list to gnutls_pcert_st* and allocates
+ * space for them.
+ */
+static gnutls_pcert_st *alloc_and_load_x509_certs(gnutls_x509_crt_t *
+ certs, unsigned ncerts)
+{
+ gnutls_pcert_st *local_certs;
+ int ret = 0;
+ unsigned i, j;
+
+ if (certs == NULL)
+ return NULL;
+
+ local_certs = gnutls_malloc(sizeof(gnutls_pcert_st) * ncerts);
+ if (local_certs == NULL) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ for (i = 0; i < ncerts; i++) {
+ ret = gnutls_pcert_import_x509(&local_certs[i], certs[i], 0);
+ if (ret < 0)
+ break;
+ }
+
+ if (ret < 0) {
+ gnutls_assert();
+ for (j = 0; j < i; j++) {
+ gnutls_pcert_deinit(&local_certs[j]);
+ }
+ gnutls_free(local_certs);
+ return NULL;
+ }
+
+ return local_certs;
+}
+
+/* converts the given x509 key to gnutls_privkey* and allocates
+ * space for it.
+ */
+static gnutls_privkey_t
+alloc_and_load_x509_key(gnutls_x509_privkey_t key, int deinit)
+{
+ gnutls_privkey_t local_key;
+ int ret = 0;
+
+ if (key == NULL)
+ return NULL;
+
+ ret = gnutls_privkey_init(&local_key);
+ if (ret < 0) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ ret =
+ gnutls_privkey_import_x509(local_key, key,
+ deinit ?
+ GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE : 0);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_privkey_deinit(local_key);
+ return NULL;
+ }
+
+ return local_key;
+}
+
+#ifdef ENABLE_PKCS11
+
+/* converts the given raw key to gnutls_privkey* and allocates
+ * space for it.
+ */
+static gnutls_privkey_t
+alloc_and_load_pkcs11_key(gnutls_pkcs11_privkey_t key, int deinit)
+{
+ gnutls_privkey_t local_key;
+ int ret = 0;
+
+ if (key == NULL)
+ return NULL;
+
+ ret = gnutls_privkey_init(&local_key);
+ if (ret < 0) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ ret =
+ gnutls_privkey_import_pkcs11(local_key, key,
+ deinit ?
+ GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE
+ : 0);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_privkey_deinit(local_key);
+ return NULL;
+ }
+
+ return local_key;
+}
+
+#endif
+
/**
* gnutls_certificate_server_set_request:
* @session: is a #gnutls_session_t type.
@@ -254,6 +358,102 @@ gnutls_certificate_server_set_request(gnutls_session_t session,
session->internals.send_cert_req = req;
}
+static int call_legacy_cert_cb1(gnutls_session_t session,
+ const struct gnutls_cert_retr_st *info,
+ gnutls_pcert_st **certs,
+ unsigned int *pcert_length,
+ gnutls_datum_t **ocsp,
+ unsigned int *ocsp_length,
+ gnutls_privkey_t *privkey,
+ unsigned int *flags)
+{
+ gnutls_retr2_st st2;
+ gnutls_pcert_st *local_certs = NULL;
+ gnutls_privkey_t local_key = NULL;
+ unsigned i;
+ int ret;
+
+ *ocsp_length = 0;
+
+ memset(&st2, 0, sizeof(st2));
+
+ ret = info->cred->legacy_cert_cb1(session, info->req_ca_rdn, info->nreqs,
+ info->pk_algos, info->pk_algos_length,
+ &st2);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (st2.cert_type != GNUTLS_CRT_X509) {
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ local_certs =
+ alloc_and_load_x509_certs(st2.cert.x509, st2.ncerts);
+ if (local_certs == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ switch (st2.key_type) {
+#ifdef ENABLE_PKCS11
+ case GNUTLS_PRIVKEY_PKCS11:
+ if (st2.key.pkcs11 != NULL) {
+ local_key =
+ alloc_and_load_pkcs11_key(st2.key.pkcs11,
+ st2.deinit_all);
+ if (local_key == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_INTERNAL_ERROR;
+ goto cleanup;
+ }
+ }
+ break;
+#endif
+ case GNUTLS_PRIVKEY_X509:
+ if (st2.key.x509 != NULL) {
+ local_key =
+ alloc_and_load_x509_key(st2.key.x509,
+ st2.deinit_all);
+ if (local_key == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_INTERNAL_ERROR;
+ goto cleanup;
+ }
+ }
+ break;
+ default:
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ *privkey = local_key;
+ *certs = local_certs;
+ *pcert_length = st2.ncerts;
+
+ /* flag the caller to deinitialize our values */
+ *flags |= GNUTLS_CERT_RETR_DEINIT_ALL;
+
+ ret = 0;
+
+ cleanup:
+
+ if (st2.cert_type == GNUTLS_CRT_X509) {
+ if (st2.deinit_all) {
+ for (i = 0; i < st2.ncerts; i++) {
+ gnutls_x509_crt_deinit(st2.cert.x509[i]);
+ }
+ gnutls_free(st2.cert.x509);
+ }
+ }
+
+ return ret;
+
+}
+
/**
* gnutls_certificate_set_retrieve_function:
* @cred: is a #gnutls_certificate_credentials_t type.
@@ -298,7 +498,30 @@ void gnutls_certificate_set_retrieve_function
(gnutls_certificate_credentials_t cred,
gnutls_certificate_retrieve_function * func)
{
- cred->get_cert_callback = func;
+ cred->legacy_cert_cb1 = func;
+ cred->get_cert_callback3 = call_legacy_cert_cb1;
+}
+
+static int call_legacy_cert_cb2(gnutls_session_t session,
+ const struct gnutls_cert_retr_st *info,
+ gnutls_pcert_st **certs,
+ unsigned int *pcert_length,
+ gnutls_datum_t **ocsp,
+ unsigned int *ocsp_length,
+ gnutls_privkey_t *privkey,
+ unsigned int *flags)
+{
+ int ret;
+ *ocsp_length = 0;
+ /* flags will be assumed to be zero */
+
+ ret = info->cred->legacy_cert_cb2(session, info->req_ca_rdn, info->nreqs,
+ info->pk_algos, info->pk_algos_length,
+ certs, pcert_length, privkey);
+ if (ret < 0) {
+ gnutls_assert();
+ }
+ return ret;
}
/**
@@ -350,7 +573,64 @@ void gnutls_certificate_set_retrieve_function2
(gnutls_certificate_credentials_t cred,
gnutls_certificate_retrieve_function2 * func)
{
- cred->get_cert_callback2 = func;
+ cred->legacy_cert_cb2 = func;
+ cred->get_cert_callback3 = call_legacy_cert_cb2;
+}
+
+/**
+ * gnutls_certificate_set_retrieve_function3:
+ * @cred: is a #gnutls_certificate_credentials_t type.
+ * @func: is the callback function
+ *
+ * This function sets a callback to be called in order to retrieve the
+ * certificate and OCSP responses to be used in the handshake. The callback will
+ * take control only if a certificate is requested by the peer.
+ *
+ * The callback's function prototype is defined in `abstract.h':
+ * int (*callback)(gnutls_session_t, const struct gnutls_cert_retr_st *info,
+ * gnutls_pcert_st **certs, unsigned int *pcert_length,
+ * gnutls_datum_t **ocsp, unsigned int *ocsp_length,
+ * gnutls_privkey_t * pkey, unsigned int *flags);
+ *
+ * The info field of the callback contains:
+ * @req_ca_dn which is a list with the CA names that the server considers trusted.
+ * This is a hint and typically the client should send a certificate that is signed
+ * by one of these CAs. These names, when available, are DER encoded. To get a more
+ * meaningful value use the function gnutls_x509_rdn_get().
+ * @pk_algos contains a list with server's acceptable public key algorithms.
+ * The certificate returned should support the server's given algorithms.
+ *
+ * The callback should fill-in the following values.
+ *
+ * @pcert should contain a single certificate and public key or a list of them.
+ * @pcert_length is the size of the previous list.
+ * @ocsp should contain a single OCSP response or a list of them.
+ * @ocsp_length is the size of the previous list.
+ * @pkey is the private key.
+ *
+ * If the callback function is provided then gnutls will call it, during
+ * handshake, after the certificate request message has been received,
+ * or during post-handshake.
+ *
+ * All the provided by the callback values will not be released or
+ * modified by gnutls.
+ *
+ * When this callback is set in server side, @pk_algos and @req_ca_dn are NULL.
+ *
+ * The callback function should set the certificate and OCSP response
+ * list to be sent, and return 0 on success. If no certificate was selected then
+ * the @pcert_length and @Ocsp_length should be set to zero. The return
+ * value (-1) indicates error and the handshake will be terminated. If both
+ * certificates are set in the credentials and a callback is available, the
+ * callback takes predence.
+ *
+ * Since: 3.6.xx
+ **/
+void gnutls_certificate_set_retrieve_function3
+ (gnutls_certificate_credentials_t cred,
+ gnutls_certificate_retrieve_function3 *func)
+{
+ cred->get_cert_callback3 = func;
}
/**
diff --git a/lib/ext/post_handshake.c b/lib/ext/post_handshake.c
index 9aa67e2aed..0af7e53f21 100644
--- a/lib/ext/post_handshake.c
+++ b/lib/ext/post_handshake.c
@@ -94,7 +94,7 @@ _gnutls_post_handshake_send_params(gnutls_session_t session,
if (unlikely(max == NULL))
return gnutls_assert_val(0);
- if (max->post_handshake_auth && (cred->ncerts || cred->get_cert_callback2 || cred->get_cert_callback))
+ if (max->post_handshake_auth && (cred->ncerts || cred->get_cert_callback3))
return GNUTLS_E_INT_RET_0;
else
return 0;
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index bb422ddbe9..63111a3a89 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -1085,9 +1085,17 @@ typedef struct {
gnutls_pcert_st *selected_cert_list;
int16_t selected_cert_list_length;
struct gnutls_privkey_st *selected_key;
- bool selected_need_free;
+
+ /* new callbacks such as gnutls_certificate_retrieve_function3
+ * set the selected_ocsp datum values. The older OCSP callback-based
+ * functions, set the ocsp_func. The former takes precedence when
+ * set.
+ */
+ gnutls_datum_t *selected_ocsp;
+ int16_t selected_ocsp_length;
gnutls_status_request_ocsp_func selected_ocsp_func;
void *selected_ocsp_func_ptr;
+ bool selected_need_free;
/* This holds the default version that our first
diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h
index bf29f877cd..bc97086262 100644
--- a/lib/includes/gnutls/abstract.h
+++ b/lib/includes/gnutls/abstract.h
@@ -655,7 +655,38 @@ typedef int gnutls_certificate_retrieve_function2(gnutls_session_t,
void gnutls_certificate_set_retrieve_function2
(gnutls_certificate_credentials_t cred,
- gnutls_certificate_retrieve_function2 * func);
+ gnutls_certificate_retrieve_function2 *func);
+
+struct gnutls_cert_retr_st {
+ unsigned version; /* set to 1 */
+ gnutls_certificate_credentials_t cred;
+ const gnutls_datum_t *req_ca_rdn;
+ unsigned nreqs;
+ const gnutls_pk_algorithm_t *pk_algos;
+ unsigned pk_algos_length;
+
+ /* other fields may be added if version is > 1 */
+ unsigned char padding[64];
+};
+
+/* When the callback sets this value, gnutls will deinitialize the given
+ * values after use */
+#define GNUTLS_CERT_RETR_DEINIT_ALL 1
+
+typedef int gnutls_certificate_retrieve_function3(
+ gnutls_session_t,
+ const struct gnutls_cert_retr_st *info,
+ gnutls_pcert_st **certs,
+ unsigned int *pcert_length,
+ gnutls_datum_t **ocsp,
+ unsigned int *ocsp_length,
+ gnutls_privkey_t *privkey,
+ unsigned int *flags);
+
+
+void gnutls_certificate_set_retrieve_function3
+ (gnutls_certificate_credentials_t cred,
+ gnutls_certificate_retrieve_function3 *func);
int
gnutls_certificate_set_key(gnutls_certificate_credentials_t res,
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index d29d650a81..e861aeaf7d 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1214,6 +1214,7 @@ GNUTLS_3_6_xx
gnutls_ocsp_resp_import2;
gnutls_ocsp_resp_export2;
gnutls_ocsp_resp_list_import2;
+ gnutls_certificate_retrieve_function3;
} GNUTLS_3_6_2;
GNUTLS_FIPS140_3_4 {