diff options
Diffstat (limited to 'lib/cert.c')
-rw-r--r-- | lib/cert.c | 284 |
1 files changed, 282 insertions, 2 deletions
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; } /** |