From 024e998f8b73047eb2ae56ea92cb2cbc0bbedc17 Mon Sep 17 00:00:00 2001 From: defanor Date: Sat, 2 Oct 2021 20:23:38 +0300 Subject: Make libgsasl optional Only EXTERNAL and PLAIN mechanisms are supported without it for now. --- src/rexmpp_sasl.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 src/rexmpp_sasl.c (limited to 'src/rexmpp_sasl.c') diff --git a/src/rexmpp_sasl.c b/src/rexmpp_sasl.c new file mode 100644 index 0000000..7dd16ba --- /dev/null +++ b/src/rexmpp_sasl.c @@ -0,0 +1,252 @@ +/** + @file rexmpp_sasl.c + @brief SASL + @author defanor + @date 2021 + @copyright MIT license. + +*/ + +#include + +#include "config.h" +#include "rexmpp.h" +#include "rexmpp_sasl.h" +#include "rexmpp_base64.h" + +#ifdef HAVE_GSASL +#include + +int rexmpp_sasl_cb (Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop) { + (void)sctx; /* The session should already be in rexmpp_t. */ + rexmpp_t *s = gsasl_callback_hook_get(ctx); + if (s == NULL || s->sasl_property_cb == NULL) { + return GSASL_NO_CALLBACK; + } + if (s->sasl_property_cb(s, (rexmpp_sasl_property)prop) == 0) { + return GSASL_OK; + } else { + return GSASL_NO_CALLBACK; + } +} + +int rexmpp_sasl_ctx_init (rexmpp_t *s) { + int err = gsasl_init(&(s->sasl.ctx)); + if (err != GSASL_OK) { + rexmpp_log(s, LOG_CRIT, "gsasl initialisation error: %s", + gsasl_strerror(err)); + return -1; + } + gsasl_callback_hook_set(s->sasl.ctx, s); + gsasl_callback_set(s->sasl.ctx, rexmpp_sasl_cb); + return 0; +} + +void rexmpp_sasl_ctx_deinit (rexmpp_t *s) { + gsasl_done(s->sasl.ctx); +} + +void rexmpp_sasl_ctx_cleanup (rexmpp_t *s) { + gsasl_finish(s->sasl.session); + s->sasl.session = NULL; +} + +int rexmpp_sasl_encode (rexmpp_t *s, const char *in, size_t in_len, char **out, size_t *out_len) { + int sasl_err = gsasl_encode (s->sasl.session, in, in_len, out, out_len); + if (sasl_err != GSASL_OK) { + rexmpp_log(s, LOG_ERR, "SASL encoding error: %s", gsasl_strerror(sasl_err)); + return -1; + } + return 0; +} + +int rexmpp_sasl_decode (rexmpp_t *s, const char *in, size_t in_len, char **out, size_t *out_len) { + int sasl_err = gsasl_decode(s->sasl.session, in, in_len, out, out_len); + if (sasl_err != GSASL_OK) { + rexmpp_log(s, LOG_ERR, "SASL decoding error: %s", gsasl_strerror(sasl_err)); + return -1; + } + return 0; +} + +const char *rexmpp_sasl_suggest_mechanism (rexmpp_t *s, const char *mech_list) { + return gsasl_client_suggest_mechanism(s->sasl.ctx, mech_list); +} + +void rexmpp_sasl_property_set (rexmpp_t *s, rexmpp_sasl_property prop, const char *data) { + gsasl_property_set (s->sasl.session, (Gsasl_property)prop, data); +} + +int rexmpp_sasl_start (rexmpp_t *s, const char *mech) { + int sasl_err = gsasl_client_start(s->sasl.ctx, mech, &(s->sasl.session)); + if (sasl_err != GSASL_OK) { + rexmpp_log(s, LOG_CRIT, "Failed to initialise SASL session: %s", + gsasl_strerror(sasl_err)); + return -1; + } + return 0; +} + +int rexmpp_sasl_step64 (rexmpp_t *s, const char *b64_in, char **b64_out) { + int sasl_err = gsasl_step64 (s->sasl.session, b64_in, b64_out); + if (sasl_err != GSASL_OK) { + if (sasl_err == GSASL_NEEDS_MORE) { + rexmpp_log(s, LOG_DEBUG, "SASL needs more data"); + } else { + rexmpp_log(s, LOG_ERR, "SASL error: %s", gsasl_strerror(sasl_err)); + return -1; + } + } + return 0; +} + +#else + +/* No GSASL. */ +#include + +int rexmpp_sasl_ctx_init (rexmpp_t *s) { + s->sasl.mech = REXMPP_SASL_MECH_UNKNOWN; + s->sasl.authid = NULL; + s->sasl.password = NULL; + return 0; +} + +void rexmpp_sasl_ctx_cleanup (rexmpp_t *s) { + s->sasl.mech = REXMPP_SASL_MECH_UNKNOWN; + if (s->sasl.authid != NULL) { + free(s->sasl.authid); + s->sasl.authid = NULL; + } + if (s->sasl.password != NULL) { + free(s->sasl.password); + s->sasl.password = NULL; + } +} + +void rexmpp_sasl_ctx_deinit (rexmpp_t *s) { + (void)s; +} + +int rexmpp_sasl_encode (rexmpp_t *s, const char *in, size_t in_len, char **out, size_t *out_len) { + (void)s; + *out = malloc(in_len); + memcpy(*out, in, in_len); + *out_len = in_len; + return 0; +} +int rexmpp_sasl_decode (rexmpp_t *s, const char *in, size_t in_len, char **out, size_t *out_len) { + (void)s; + *out = malloc(in_len); + memcpy(*out, in, in_len); + *out_len = in_len; + return 0; +} + +rexmpp_sasl_mechanism rexmpp_sasl_mech_read (const char *mech) { + if (mech == NULL) { + return REXMPP_SASL_MECH_UNKNOWN; + } + if (strcmp(mech, "EXTERNAL") == 0) { + return REXMPP_SASL_MECH_EXTERNAL; + } else if (strcmp(mech, "PLAIN") == 0) { + return REXMPP_SASL_MECH_PLAIN; + } else { + return REXMPP_SASL_MECH_UNKNOWN; + } +} + +const char *rexmpp_sasl_mech_name (rexmpp_sasl_mechanism mech) { + if (mech == REXMPP_SASL_MECH_EXTERNAL) { + return "EXTERNAL"; + } else if (mech == REXMPP_SASL_MECH_PLAIN) { + return "PLAIN"; + } else { + return NULL; + } +} + +const char *rexmpp_sasl_suggest_mechanism (rexmpp_t *s, const char *mech_list) { + (void)s; + char *mech, *save_ptr, *mlist = strdup(mech_list); + mech = strtok_r(mlist, " ", &save_ptr); + rexmpp_sasl_mechanism preferred = REXMPP_SASL_MECH_UNKNOWN; + while (mech != NULL) { + rexmpp_sasl_mechanism m = rexmpp_sasl_mech_read(mech); + if (m == REXMPP_SASL_MECH_EXTERNAL || + (m == REXMPP_SASL_MECH_PLAIN && preferred == REXMPP_SASL_MECH_UNKNOWN)) { + preferred = m; + } + mech = strtok_r(NULL, " ", &save_ptr); + } + free(mlist); + return rexmpp_sasl_mech_name(preferred); +} + +int rexmpp_sasl_start (rexmpp_t *s, const char *mech) { + rexmpp_sasl_mechanism m = rexmpp_sasl_mech_read(mech); + if (m != REXMPP_SASL_MECH_UNKNOWN) { + s->sasl.mech = m; + return 0; + } + return -1; +} + +const char *rexmpp_sasl_get_prop (rexmpp_t *s, rexmpp_sasl_property prop) { + if (prop == REXMPP_SASL_PROP_AUTHID) { + if (s->sasl.authid == NULL) { + s->sasl_property_cb(s, prop); + } + return s->sasl.authid; + } else if (prop == REXMPP_SASL_PROP_PASSWORD) { + if (s->sasl.password == NULL) { + s->sasl_property_cb(s, prop); + } + return s->sasl.password; + } + return NULL; +} + +int rexmpp_sasl_step64 (rexmpp_t *s, const char *b64_in, char **b64_out) { + (void)s; + (void)b64_in; + if (s->sasl.mech == REXMPP_SASL_MECH_PLAIN) { + /* RFC 4616 */ + const char *authid = rexmpp_sasl_get_prop(s, REXMPP_SASL_PROP_AUTHID); + const char *password = rexmpp_sasl_get_prop(s, REXMPP_SASL_PROP_PASSWORD); + if (authid != NULL && password != NULL) { + size_t auth_len = strlen(authid) + strlen(password) + 2; + char *auth = malloc(auth_len); + auth[0] = 0; + memcpy(auth + 1, authid, strlen(authid)); + auth[strlen(authid) + 1] = 0; + memcpy(auth + strlen(authid) + 2, password, strlen(password)); + size_t out_len; + rexmpp_base64_to(auth, auth_len, b64_out, &out_len); + free(auth); + return 0; + } + } else if (s->sasl.mech == REXMPP_SASL_MECH_EXTERNAL) { + *b64_out = strdup(""); + return 0; + } + return -1; +} + +void rexmpp_sasl_property_set (rexmpp_t *s, rexmpp_sasl_property prop, const char *data) { + (void)s; + (void)data; + if (prop == REXMPP_SASL_PROP_AUTHID) { + if (s->sasl.authid != NULL) { + free(s->sasl.authid); + } + s->sasl.authid = strdup(data); + } else if (prop == REXMPP_SASL_PROP_PASSWORD) { + if (s->sasl.password != NULL) { + free(s->sasl.password); + } + s->sasl.password = strdup(data); + } +} + +#endif -- cgit v1.2.3