add tls_id_on_xmppaddr() for GnuTLS
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
This commit is contained in:
committed by
Dmitry Podgorny
parent
9a0e070c24
commit
27be6acaeb
143
src/tls_gnutls.c
143
src/tls_gnutls.c
@@ -14,6 +14,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
|
#include <gnutls/x509.h>
|
||||||
|
#include <gnutls/x509-ext.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
@@ -27,6 +29,7 @@ struct _tls {
|
|||||||
sock_t sock;
|
sock_t sock;
|
||||||
gnutls_session_t session;
|
gnutls_session_t session;
|
||||||
gnutls_certificate_credentials_t cred;
|
gnutls_certificate_credentials_t cred;
|
||||||
|
gnutls_x509_crt_t client_cert;
|
||||||
int lasterror;
|
int lasterror;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,6 +49,130 @@ void tls_shutdown(void)
|
|||||||
gnutls_global_deinit();
|
gnutls_global_deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gnutls_x509_crt_t _tls_load_cert(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
if (conn->tls && conn->tls->client_cert)
|
||||||
|
return conn->tls->client_cert;
|
||||||
|
gnutls_x509_crt_t cert;
|
||||||
|
gnutls_datum_t data;
|
||||||
|
int res;
|
||||||
|
if (gnutls_x509_crt_init(&cert) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (gnutls_load_file(conn->tls_client_cert, &data) < 0)
|
||||||
|
goto LBL_ERR;
|
||||||
|
res = gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM);
|
||||||
|
gnutls_free(data.data);
|
||||||
|
if (res < 0)
|
||||||
|
goto LBL_ERR;
|
||||||
|
return cert;
|
||||||
|
LBL_ERR:
|
||||||
|
gnutls_x509_crt_deinit(cert);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tls_free_cert(xmpp_conn_t *conn, gnutls_x509_crt_t cert)
|
||||||
|
{
|
||||||
|
if (conn->tls && conn->tls->client_cert == cert)
|
||||||
|
return;
|
||||||
|
gnutls_x509_crt_deinit(cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _tls_get_id_on_xmppaddr(xmpp_conn_t *conn,
|
||||||
|
gnutls_x509_crt_t cert,
|
||||||
|
unsigned int n,
|
||||||
|
char **ret)
|
||||||
|
{
|
||||||
|
gnutls_datum_t san;
|
||||||
|
size_t name_len, oid_len;
|
||||||
|
char oid[128], name[128];
|
||||||
|
name_len = oid_len = 128;
|
||||||
|
int res =
|
||||||
|
gnutls_x509_crt_get_subject_alt_name(cert, n, name, &name_len, NULL);
|
||||||
|
if (res == GNUTLS_SAN_OTHERNAME_XMPP) {
|
||||||
|
/* This is the happy flow path with fixed GnuTLS.
|
||||||
|
* While implementing this I stumbled over an issue in GnuTLS
|
||||||
|
* which lead to
|
||||||
|
* https://gitlab.com/gnutls/gnutls/-/merge_requests/1397
|
||||||
|
*/
|
||||||
|
if (ret) {
|
||||||
|
*ret = xmpp_strdup(conn->ctx, name);
|
||||||
|
}
|
||||||
|
return GNUTLS_SAN_OTHERNAME_XMPP;
|
||||||
|
}
|
||||||
|
if (res == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
|
||||||
|
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
|
||||||
|
if (res != GNUTLS_SAN_OTHERNAME)
|
||||||
|
return GNUTLS_E_X509_UNKNOWN_SAN;
|
||||||
|
res = gnutls_x509_crt_get_subject_alt_othername_oid(cert, n, oid, &oid_len);
|
||||||
|
if (res == GNUTLS_SAN_OTHERNAME_XMPP) {
|
||||||
|
gnutls_datum_t xmpp_addr;
|
||||||
|
san.data = (unsigned char *)name;
|
||||||
|
san.size = name_len;
|
||||||
|
res = gnutls_x509_othername_to_virtual(oid, &san, NULL, &xmpp_addr);
|
||||||
|
if (res < 0) {
|
||||||
|
gnutls_free(xmpp_addr.data);
|
||||||
|
return GNUTLS_E_MEMORY_ERROR;
|
||||||
|
}
|
||||||
|
if (ret) {
|
||||||
|
*ret = xmpp_strdup(conn->ctx, (char *)xmpp_addr.data);
|
||||||
|
}
|
||||||
|
gnutls_free(xmpp_addr.data);
|
||||||
|
return GNUTLS_SAN_OTHERNAME_XMPP;
|
||||||
|
}
|
||||||
|
return GNUTLS_E_X509_UNKNOWN_SAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _tls_id_on_xmppaddr(xmpp_conn_t *conn,
|
||||||
|
gnutls_x509_crt_t cert,
|
||||||
|
unsigned int n,
|
||||||
|
char **ret)
|
||||||
|
{
|
||||||
|
int res = GNUTLS_E_SUCCESS;
|
||||||
|
unsigned int i, j;
|
||||||
|
for (i = j = 0; res != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; ++i) {
|
||||||
|
res = _tls_get_id_on_xmppaddr(conn, cert, i, NULL);
|
||||||
|
xmpp_debug(conn->ctx, "tls", "orly %i res=%i", i, res);
|
||||||
|
if (res == GNUTLS_SAN_OTHERNAME_XMPP) {
|
||||||
|
if (j == n) {
|
||||||
|
res = _tls_get_id_on_xmppaddr(conn, cert, i, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search through the SubjectAlternativeNames and return the next
|
||||||
|
* id-on-xmppAddr element starting from `n`.
|
||||||
|
*/
|
||||||
|
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n)
|
||||||
|
{
|
||||||
|
char *ret = NULL;
|
||||||
|
gnutls_x509_crt_t cert = _tls_load_cert(conn);
|
||||||
|
if (cert == NULL)
|
||||||
|
return NULL;
|
||||||
|
_tls_id_on_xmppaddr(conn, cert, n, &ret);
|
||||||
|
_tls_free_cert(conn, cert);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
unsigned int ret = 0, n;
|
||||||
|
int res = GNUTLS_E_SUCCESS;
|
||||||
|
gnutls_x509_crt_t cert = _tls_load_cert(conn);
|
||||||
|
if (cert == NULL)
|
||||||
|
return 0;
|
||||||
|
for (n = 0; res != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; ++n) {
|
||||||
|
res = _tls_id_on_xmppaddr(conn, cert, n, NULL);
|
||||||
|
if (res == GNUTLS_SAN_OTHERNAME_XMPP)
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
_tls_free_cert(conn, cert);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
tls_t *tls_new(xmpp_conn_t *conn)
|
tls_t *tls_new(xmpp_conn_t *conn)
|
||||||
{
|
{
|
||||||
tls_t *tls = xmpp_alloc(conn->ctx, sizeof(tls_t));
|
tls_t *tls = xmpp_alloc(conn->ctx, sizeof(tls_t));
|
||||||
@@ -58,6 +185,21 @@ tls_t *tls_new(xmpp_conn_t *conn)
|
|||||||
gnutls_certificate_allocate_credentials(&tls->cred);
|
gnutls_certificate_allocate_credentials(&tls->cred);
|
||||||
tls_set_credentials(tls, CAFILE);
|
tls_set_credentials(tls, CAFILE);
|
||||||
|
|
||||||
|
if (conn->tls_client_cert && conn->tls_client_key) {
|
||||||
|
tls->client_cert = _tls_load_cert(conn);
|
||||||
|
if (!tls->client_cert) {
|
||||||
|
xmpp_error(tls->ctx, "tls",
|
||||||
|
"could not read client certificate");
|
||||||
|
gnutls_certificate_free_credentials(tls->cred);
|
||||||
|
gnutls_deinit(tls->session);
|
||||||
|
xmpp_free(tls->ctx, tls);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
gnutls_certificate_set_x509_key_file(
|
||||||
|
tls->cred, conn->tls_client_cert, conn->tls_client_key,
|
||||||
|
GNUTLS_X509_FMT_PEM);
|
||||||
|
}
|
||||||
|
|
||||||
gnutls_set_default_priority(tls->session);
|
gnutls_set_default_priority(tls->session);
|
||||||
|
|
||||||
/* fixme: this may require setting a callback on win32? */
|
/* fixme: this may require setting a callback on win32? */
|
||||||
@@ -69,6 +211,7 @@ tls_t *tls_new(xmpp_conn_t *conn)
|
|||||||
|
|
||||||
void tls_free(tls_t *tls)
|
void tls_free(tls_t *tls)
|
||||||
{
|
{
|
||||||
|
gnutls_x509_crt_deinit(tls->client_cert);
|
||||||
gnutls_deinit(tls->session);
|
gnutls_deinit(tls->session);
|
||||||
gnutls_certificate_free_credentials(tls->cred);
|
gnutls_certificate_free_credentials(tls->cred);
|
||||||
xmpp_free(tls->ctx, tls);
|
xmpp_free(tls->ctx, tls);
|
||||||
|
|||||||
Reference in New Issue
Block a user