add callback functionality on certificate verification failure
Based on the differences to libmesode this functionality has been added. It allows a library-user to set a callback for cases where the TLS stack can't verify a received certificate and let the end-user decide what to do. examples/basic implements an example handler of said functionality. Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
This commit is contained in:
@@ -169,6 +169,8 @@ struct _xmpp_conn_t {
|
||||
int tls_mandatory;
|
||||
int tls_legacy_ssl;
|
||||
int tls_trust;
|
||||
char *tls_cafile;
|
||||
char *tls_capath;
|
||||
char *tls_client_cert;
|
||||
char *tls_client_key;
|
||||
int tls_failed; /* set when tls fails, so we don't try again */
|
||||
@@ -176,6 +178,7 @@ struct _xmpp_conn_t {
|
||||
mechanisms */
|
||||
int auth_legacy_enabled;
|
||||
int secured; /* set when stream is secured with TLS */
|
||||
xmpp_certfail_handler certfail_handler;
|
||||
|
||||
/* if server returns <bind/> or <session/> we must do them */
|
||||
int bind_required;
|
||||
|
||||
68
src/conn.c
68
src/conn.c
@@ -144,11 +144,14 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx)
|
||||
conn->tls_legacy_ssl = 0;
|
||||
conn->tls_trust = 0;
|
||||
conn->tls_failed = 0;
|
||||
conn->tls_cafile = NULL;
|
||||
conn->tls_capath = NULL;
|
||||
conn->tls_client_cert = NULL;
|
||||
conn->tls_client_key = NULL;
|
||||
conn->sasl_support = 0;
|
||||
conn->auth_legacy_enabled = 0;
|
||||
conn->secured = 0;
|
||||
conn->certfail_handler = NULL;
|
||||
|
||||
conn->bind_required = 0;
|
||||
conn->session_required = 0;
|
||||
@@ -345,6 +348,10 @@ int xmpp_conn_release(xmpp_conn_t *conn)
|
||||
xmpp_free(ctx, conn->tls_client_cert);
|
||||
if (conn->tls_client_key)
|
||||
xmpp_free(ctx, conn->tls_client_key);
|
||||
if (conn->tls_cafile)
|
||||
xmpp_free(ctx, conn->tls_cafile);
|
||||
if (conn->tls_capath)
|
||||
xmpp_free(ctx, conn->tls_capath);
|
||||
xmpp_free(ctx, conn);
|
||||
released = 1;
|
||||
}
|
||||
@@ -401,6 +408,64 @@ void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid)
|
||||
conn->jid = xmpp_strdup(conn->ctx, jid);
|
||||
}
|
||||
|
||||
/** Set the Handler function which will be called when the TLS stack can't
|
||||
* verify the CA of the server we're trying to connect to.
|
||||
*
|
||||
* @param conn a Strophe connection object
|
||||
* @param hndl certfail Handler function
|
||||
*
|
||||
* @ingroup Connections
|
||||
* @ingroup TLS
|
||||
*/
|
||||
void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
|
||||
xmpp_certfail_handler hndl)
|
||||
{
|
||||
conn->certfail_handler = hndl;
|
||||
}
|
||||
|
||||
/** Set the CAfile
|
||||
*
|
||||
* @param conn a Strophe connection object
|
||||
* @param cert path to a certificate file
|
||||
*
|
||||
* @ingroup Connections
|
||||
* @ingroup TLS
|
||||
*/
|
||||
void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path)
|
||||
{
|
||||
conn->tls_cafile = xmpp_strdup(conn->ctx, path);
|
||||
}
|
||||
|
||||
/** Set the CApath
|
||||
*
|
||||
* @param conn a Strophe connection object
|
||||
* @param cert path to a folder containing certificates
|
||||
*
|
||||
* @ingroup Connections
|
||||
* @ingroup TLS
|
||||
*/
|
||||
void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path)
|
||||
{
|
||||
conn->tls_capath = xmpp_strdup(conn->ctx, path);
|
||||
}
|
||||
|
||||
/** Retrieve the peer certificate
|
||||
*
|
||||
* The returned Certificate object must be free'd by calling
|
||||
* \ref xmpp_tlscert_free
|
||||
*
|
||||
* @param conn a Strophe connection object
|
||||
*
|
||||
* @return a Strophe Certificate object
|
||||
*
|
||||
* @ingroup Connections
|
||||
* @ingroup TLS
|
||||
*/
|
||||
xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn)
|
||||
{
|
||||
return tls_peer_cert(conn);
|
||||
}
|
||||
|
||||
/** Set the Client Certificate and Private Key that will be bound to the
|
||||
* connection. If any of the both was previously set, it will be discarded.
|
||||
* This should not be used after a connection is created. The function will
|
||||
@@ -412,6 +477,7 @@ void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid)
|
||||
* @param key path to a private key file
|
||||
*
|
||||
* @ingroup Connections
|
||||
* @ingroup TLS
|
||||
*/
|
||||
void xmpp_conn_set_client_cert(xmpp_conn_t *const conn,
|
||||
const char *const cert,
|
||||
@@ -433,6 +499,7 @@ void xmpp_conn_set_client_cert(xmpp_conn_t *const conn,
|
||||
* @return the number of xmppAddr entries in the client certificate
|
||||
*
|
||||
* @ingroup Connections
|
||||
* @ingroup TLS
|
||||
*/
|
||||
unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *const conn)
|
||||
{
|
||||
@@ -447,6 +514,7 @@ unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *const conn)
|
||||
* @return a string containing the xmppAddr or NULL if n is out of range
|
||||
*
|
||||
* @ingroup Connections
|
||||
* @ingroup TLS
|
||||
*/
|
||||
char *xmpp_conn_cert_xmppaddr(xmpp_conn_t *const conn, unsigned int n)
|
||||
{
|
||||
|
||||
214
src/tls.c
Normal file
214
src/tls.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/* tls.c
|
||||
** strophe XMPP client library -- generic TLS functions
|
||||
**
|
||||
** Copyright (C) 2021 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Generic TLS functionality.
|
||||
*/
|
||||
|
||||
/** @defgroup TLS SSL/TLS specific functionality
|
||||
* These functions provide SSL/TLS specific functionality.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "strophe.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
struct _dnsname_t {
|
||||
char **data;
|
||||
size_t cur, max;
|
||||
};
|
||||
|
||||
const size_t tlscert_dnsnames_increment = 4;
|
||||
|
||||
/** Get the Strophe context which is assigned to this certificate.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
*
|
||||
* @return the Strophe context object where this certificate originates from
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
xmpp_ctx_t *xmpp_tlscert_get_ctx(const xmpp_tlscert_t *cert)
|
||||
{
|
||||
return cert->ctx;
|
||||
}
|
||||
|
||||
/** Get the Strophe connection which is assigned to this certificate.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
*
|
||||
* @return the Strophe connection object where this certificate originates from
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
xmpp_conn_t *xmpp_tlscert_get_conn(const xmpp_tlscert_t *cert)
|
||||
{
|
||||
return cert->conn;
|
||||
}
|
||||
|
||||
/** Get the complete PEM of this certificate.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
*
|
||||
* @return a string containing the PEM of this certificate
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
const char *xmpp_tlscert_get_pem(const xmpp_tlscert_t *cert)
|
||||
{
|
||||
return cert->pem;
|
||||
}
|
||||
|
||||
/** Get the dnsName entries out of the SubjectAlternativeNames.
|
||||
*
|
||||
* Note: Max. `MAX_NUM_DNSNAMES` are supported.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
* @param n which dnsName entry
|
||||
*
|
||||
* @return a string with the n'th dnsName
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
const char *xmpp_tlscert_get_dnsname(const xmpp_tlscert_t *cert, size_t n)
|
||||
{
|
||||
if (n >= cert->dnsnames->cur)
|
||||
return NULL;
|
||||
return cert->dnsnames->data[n];
|
||||
}
|
||||
|
||||
/** Get various parts of the certificate as String.
|
||||
*
|
||||
* c.f. \ref xmpp_cert_element_t for details.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
* @param elmnt which part of the certificate
|
||||
*
|
||||
* @return a string with the part of the certificate
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
const char *xmpp_tlscert_get_string(const xmpp_tlscert_t *cert,
|
||||
xmpp_cert_element_t elmnt)
|
||||
{
|
||||
if (elmnt >= XMPP_CERT_ELEMENT_MAX)
|
||||
return NULL;
|
||||
return cert->elements[elmnt];
|
||||
}
|
||||
|
||||
/** Get a descriptive string for each xmpp_cert_element_t.
|
||||
*
|
||||
* c.f. \ref xmpp_cert_element_t for details.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
* @param elmnt which element
|
||||
*
|
||||
* @return a string with the description
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
const char *xmpp_tlscert_get_description(xmpp_cert_element_t elmnt)
|
||||
{
|
||||
static const char *descriptions[] = {
|
||||
"X.509 Version",
|
||||
"SerialNumber",
|
||||
"Subject",
|
||||
"Issuer",
|
||||
"Issued On",
|
||||
"Expires On",
|
||||
"Public Key Algorithm",
|
||||
"Certificate Signature Algorithm",
|
||||
"Fingerprint SHA-1",
|
||||
"Fingerprint SHA-256",
|
||||
};
|
||||
if (elmnt >= XMPP_CERT_ELEMENT_MAX)
|
||||
return NULL;
|
||||
return descriptions[elmnt];
|
||||
}
|
||||
|
||||
/** Allocate and initialize a Strophe TLS certificate object.
|
||||
*
|
||||
* @param ctx a Strophe context object
|
||||
*
|
||||
* @return a certificate object or NULL
|
||||
*/
|
||||
xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_tlscert_t *tlscert = xmpp_alloc(ctx, sizeof(*tlscert));
|
||||
if (!tlscert)
|
||||
return NULL;
|
||||
memset(tlscert, 0, sizeof(*tlscert));
|
||||
|
||||
tlscert->dnsnames = xmpp_alloc(ctx, sizeof(*tlscert->dnsnames));
|
||||
if (!tlscert->dnsnames) {
|
||||
xmpp_free(ctx, tlscert);
|
||||
return NULL;
|
||||
}
|
||||
memset(tlscert->dnsnames, 0, sizeof(*tlscert->dnsnames));
|
||||
|
||||
tlscert->ctx = ctx;
|
||||
|
||||
return tlscert;
|
||||
}
|
||||
|
||||
/** Free a certificate object.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
*
|
||||
* @ingroup TLS
|
||||
*/
|
||||
void xmpp_tlscert_free(xmpp_tlscert_t *cert)
|
||||
{
|
||||
size_t n;
|
||||
for (n = 0; n < ARRAY_SIZE(cert->elements); ++n) {
|
||||
if (cert->elements[n])
|
||||
xmpp_free(cert->ctx, cert->elements[n]);
|
||||
}
|
||||
if (cert->dnsnames->data) {
|
||||
for (n = 0; n < cert->dnsnames->cur; ++n) {
|
||||
if (cert->dnsnames->data[n])
|
||||
xmpp_free(cert->ctx, cert->dnsnames->data[n]);
|
||||
}
|
||||
}
|
||||
xmpp_free(cert->ctx, cert->dnsnames->data);
|
||||
xmpp_free(cert->ctx, cert->dnsnames);
|
||||
if (cert->pem)
|
||||
xmpp_free(cert->ctx, cert->pem);
|
||||
xmpp_free(cert->ctx, cert);
|
||||
}
|
||||
|
||||
/** Add a dnsName to the Strophe TLS certificate object.
|
||||
*
|
||||
* @param cert a Strophe TLS certificate object
|
||||
* @param dnsname dnsName that shall be stored
|
||||
*
|
||||
* @return classic Unix style - 0=success, 1=error
|
||||
*/
|
||||
int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname)
|
||||
{
|
||||
if ((cert->dnsnames->cur + 1) >= cert->dnsnames->max) {
|
||||
char **dnsnames =
|
||||
xmpp_realloc(cert->ctx, cert->dnsnames->data,
|
||||
(cert->dnsnames->max + tlscert_dnsnames_increment) *
|
||||
sizeof(char **));
|
||||
if (!dnsnames)
|
||||
return 1;
|
||||
cert->dnsnames->data = dnsnames;
|
||||
cert->dnsnames->max += tlscert_dnsnames_increment;
|
||||
}
|
||||
cert->dnsnames->data[cert->dnsnames->cur++] =
|
||||
xmpp_strdup(cert->ctx, dnsname);
|
||||
return 0;
|
||||
}
|
||||
14
src/tls.h
14
src/tls.h
@@ -21,6 +21,16 @@
|
||||
|
||||
typedef struct _tls tls_t;
|
||||
|
||||
typedef struct _dnsname_t dnsname_t;
|
||||
|
||||
struct _xmpp_tlscert_t {
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
char *pem;
|
||||
char *elements[XMPP_CERT_ELEMENT_MAX];
|
||||
dnsname_t *dnsnames;
|
||||
};
|
||||
|
||||
void tls_initialize(void);
|
||||
void tls_shutdown(void);
|
||||
|
||||
@@ -30,6 +40,7 @@ void tls_free(tls_t *tls);
|
||||
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n);
|
||||
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn);
|
||||
|
||||
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn);
|
||||
int tls_set_credentials(tls_t *tls, const char *cafilename);
|
||||
|
||||
int tls_start(tls_t *tls);
|
||||
@@ -44,4 +55,7 @@ int tls_write(tls_t *tls, const void *buff, size_t len);
|
||||
int tls_clear_pending_write(tls_t *tls);
|
||||
int tls_is_recoverable(int error);
|
||||
|
||||
xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx);
|
||||
int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname);
|
||||
|
||||
#endif /* __LIBSTROPHE_TLS_H__ */
|
||||
|
||||
Reference in New Issue
Block a user