diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2016-09-07 14:49:35 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2016-09-12 09:49:23 +0200 |
commit | 6b76e0c899b1ff08df9bd9b41588f771f050be89 (patch) | |
tree | 6997e6243e6319c07c59c3bcabce11695de02531 | |
parent | 3026dfb211d75c3d1085ed18304f8dffde855c1b (diff) | |
download | gnutls-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.c | 40 | ||||
-rw-r--r-- | lib/auth/cert.h | 12 | ||||
-rw-r--r-- | lib/cert.c | 2 | ||||
-rw-r--r-- | lib/ext/status_request.c | 167 | ||||
-rw-r--r-- | lib/gnutls_int.h | 2 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 7 | ||||
-rw-r--r-- | lib/libgnutls.map | 1 | ||||
-rw-r--r-- | lib/x509.c | 2 |
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; |