mbsync relied exclusively on the Cyrus SASL library (libsasl2) for the
logic to authenticate to IMAP servers via SASL.
Add support for mbsync to optionally use the GNU SASL library (libgsasl)
instead. This gives users the choice of which library to use at
configuration time, providing them flexibility with their dependency
management.
The default behaviour of using Cyrus SASL is retained. If the
--without-sasl or --with-gsasl config options are added, then GNU SASL
is used instead. If both --with-sasl and --with-gsasl are added, then
Cyrus SASL is given priority.
---
configure.ac | 56 ++++++++++++--
src/drv_imap.c | 4 +-
src/imap_auth.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++
src/imap_auth.h | 1 +
src/main.c | 4 +-
5 files changed, 256 insertions(+), 8 deletions(-)
diff --git a/configure.ac b/configure.ac
index 1cff65a07d6a..4b2d4587fbc1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -156,9 +156,21 @@ fi
AC_SUBST(SSL_LIBS)
have_sasl_paths=
+have_gsasl_paths=
AC_ARG_WITH(sasl,
- AS_HELP_STRING([--with-sasl[=PATH]], [where to look for SASL [detect]]),
- [ob_cv_with_sasl=$withval])
+ AS_HELP_STRING([--with-sasl[=PATH]], [where to look for Cyrus SASL
[detect]]),
+ [ob_cv_with_sasl=${withval:-yes}])
+AC_ARG_WITH(gsasl,
+ AS_HELP_STRING([--with-gsasl[=PATH]], [where to look for GNU SASL [detect]]),
+ [ob_cv_with_gsasl=${withval:-yes}])
+if test -n "$ob_cv_with_gsasl" && test "x$ob_cv_with_gsasl" != xno; then
+ if test -z "$ob_cv_with_sasl"; then
+ ob_cv_with_sasl=no
+ elif test "x$ob_cv_with_sasl" != xno; then
+ AC_MSG_WARN([Cyrus and GNU SASL are mutually exclusive; prioritizing Cyrus
SASL])
+ ob_cv_with_gsasl=no
+ fi
+fi
if test "x$ob_cv_with_sasl" != xno; then
case $ob_cv_with_sasl in
""|yes)
@@ -184,13 +196,45 @@ if test "x$ob_cv_with_sasl" != xno; then
if test -z "$have_sasl_paths"; then
if test -n "$ob_cv_with_sasl"; then
- AC_MSG_ERROR([SASL libs and/or includes were not found where specified])
+ AC_MSG_ERROR([Cyrus SASL libs and/or includes were not found where
specified])
fi
else
- AC_DEFINE(HAVE_LIBSASL, 1, [if you have the SASL libraries])
+ AC_DEFINE(HAVE_LIBSASL, 1, [if you have the Cyrus SASL library])
CPPFLAGS="$CPPFLAGS $SASL_CPPFLAGS"
LDFLAGS="$LDFLAGS $SASL_LDFLAGS"
fi
+elif test "x$ob_cv_with_gsasl" != xno; then
+ case $ob_cv_with_gsasl in
+ ""|yes)
+ dnl FIXME: Try various possible paths here...
+ ;;
+ *)
+ GSASL_LDFLAGS=-L$ob_cv_with_gsasl/lib$libsuff
+ GSASL_CPPFLAGS=-I$ob_cv_with_gsasl/include
+ ;;
+ esac
+ if test -z "$have_gsasl_paths"; then
+ sav_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS $GSASL_LDFLAGS"
+ AC_CHECK_LIB(gsasl, gsasl_init,
+ [SASL_LIBS="-lgsasl" have_gsasl_paths=yes])
+ LDFLAGS=$sav_LDFLAGS
+ fi
+
+ sav_CPPFLAGS=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS $GSASL_CPPFLAGS"
+ AC_CHECK_HEADER(gsasl.h, , [have_gsasl_paths=])
+ CPPFLAGS=$sav_CPPFLAGS
+
+ if test -z "$have_gsasl_paths"; then
+ if test -n "$ob_cv_with_gsasl"; then
+ AC_MSG_ERROR([GNU SASL libs and/or includes were not found where
specified])
+ fi
+ else
+ AC_DEFINE(HAVE_LIBGSASL, 1, [if you have the GNU SASL library])
+ CPPFLAGS="$CPPFLAGS $GSASL_CPPFLAGS"
+ LDFLAGS="$LDFLAGS $GSASL_LDFLAGS"
+ fi
fi
AC_SUBST(SASL_LIBS)
@@ -264,7 +308,9 @@ else
AC_MSG_RESULT([Not using SSL])
fi
if test -n "$have_sasl_paths"; then
- AC_MSG_RESULT([Using SASL])
+ AC_MSG_RESULT([Using Cyrus SASL])
+elif test -n "$have_gsasl_paths"; then
+ AC_MSG_RESULT([Using GNU SASL])
else
AC_MSG_RESULT([Not using SASL])
fi
diff --git a/src/drv_imap.c b/src/drv_imap.c
index 26b205ab8c0d..b4116f3ec958 100644
--- a/src/drv_imap.c
+++ b/src/drv_imap.c
@@ -3821,10 +3821,10 @@ imap_parse_store( conffile_t *cfg, store_conf_t
**storep )
if (require_cram)
add_string_list(&server->auth_mechs,
"CRAM-MD5");
}
-#ifndef HAVE_LIBSASL
+#if !defined(HAVE_LIBSASL) && !defined(HAVE_LIBGSASL)
for (string_list_t *mech = server->auth_mechs; mech; mech =
mech->next) {
if (strcmp( mech->string, "*" ) && strcasecmp(
mech->string, "LOGIN" )) {
- conf_error( cfg, EXE " built without LibSASL;
only AuthMech LOGIN is supported.\n" );
+ conf_error( cfg, EXE " built without SASL
support; only AuthMech LOGIN is supported.\n" );
break;
}
}
diff --git a/src/imap_auth.c b/src/imap_auth.c
index a0398b5cd7ad..f9436233d2c8 100644
--- a/src/imap_auth.c
+++ b/src/imap_auth.c
@@ -9,11 +9,15 @@
#if defined(HAVE_LIBSASL)
# include <sasl/sasl.h>
# include <sasl/saslutil.h>
+#elif defined(HAVE_LIBGSASL)
+# include <gsasl.h>
#endif
#define MIN_SASL_MECH_LEN 1
#define MAX_SASL_MECH_LEN 20
+#define REQ_GSASL_VERSION "0.2.19"
+
static const char *
cred_stub( void *arg )
{
@@ -152,6 +156,101 @@ encode_sasl_data( const char *out, uint out_len, char
**enc, uint *enc_len )
return 0;
}
+#elif defined(HAVE_LIBGSASL)
+
+static Gsasl *Ctx = NULL;
+
+/* Sorted by security (descending order). */
+static const char *AvailableMechs =
+ "SCRAM-SHA-256 "
+ "SCRAM-SHA-1 "
+ "CRAM-MD5 "
+ "NTLM "
+ "PLAIN "
+ "EXTERNAL "
+ "ANONYMOUS ";
+
+static int
+process_sasl_prop( Gsasl *ctx, Gsasl_session *session, Gsasl_property prop )
+{
+ (void)ctx;
+
+ const char *val = NULL;
+ imap_auth_client_t *client = gsasl_session_hook_get( session );
+ assert( client );
+
+ switch (prop) {
+ case GSASL_ANONYMOUS_TOKEN:
+ val = client->user_cb( client->user_arg );
+ if (!val)
+ val = "anonymous";
+ break;
+ case GSASL_AUTHID:
+ case GSASL_AUTHZID:
+ val = client->user_cb( client->user_arg );
+ break;
+ case GSASL_HOSTNAME:
+ val = client->domain;
+ break;
+ case GSASL_PASSWORD:
+ val = client->pass_cb( client->pass_arg );
+ break;
+ case GSASL_SERVICE:
+ val = "imap";
+ break;
+ default:
+ debug( "Unhandled SASL property (ID = %d)\n", (int)prop );
+ break;
+ }
+ if (!val)
+ return GSASL_NO_CALLBACK;
+
+#if GSASL_VERSION_NUMBER + 0 >= 0x010b00 /* >= v1.11.0 */
+ return gsasl_property_set( session, prop, val );
+#else
+ gsasl_property_set( session, prop, val );
+ return GSASL_OK;
+#endif
+}
+
+static int
+process_sasl_step( Gsasl_session *session, const char *in, char **out )
+{
+ assert( session );
+ assert( out );
+
+ const char *err = NULL;
+ Gsasl_rc rc = gsasl_step64( session, in, out );
+
+ switch (rc) {
+ case GSASL_OK:
+ return 0;
+ case GSASL_NEEDS_MORE:
+ return 1;
+ default:
+ err = gsasl_strerror( rc );
+ error( "Error performing SASL authentication step: %s\n", err );
+ return -1;
+ }
+}
+
+static char *
+choose_sasl_mech( const char *mechs )
+{
+ size_t amech_len = 0;
+ for (const char *amech = AvailableMechs; *amech; amech += amech_len +
1) {
+ amech_len = strchr( amech , ' ' ) - amech;
+ if (!find_mech( mechs, amech, amech_len ))
+ continue;
+
+ char *mech = nfmalloc( amech_len + 1 );
+ memcpy( mech, amech, amech_len );
+ mech[amech_len] = '\0';
+ return mech;
+ }
+ return NULL;
+}
+
#endif
int
@@ -164,6 +263,23 @@ imap_auth_init( void )
error( "Error initializing SASL library: %s\n", err );
return -1;
}
+#elif defined(HAVE_LIBGSASL)
+ const char *vers = gsasl_check_version( REQ_GSASL_VERSION );
+ if (!vers) {
+ error(
+ "Error: SASL library is outdated (must be at
least v%s)\n",
+ REQ_GSASL_VERSION );
+ return -1;
+ }
+
+ Gsasl_rc rc = gsasl_init( &Ctx );
+ if (rc != GSASL_OK) {
+ const char *err = gsasl_strerror( rc );
+ error( "Error initializing SASL library: %s\n", err );
+ return -1;
+ }
+
+ gsasl_callback_set( Ctx, process_sasl_prop );
#endif
return 0;
}
@@ -173,6 +289,9 @@ imap_auth_cleanup( void )
{
#if defined(HAVE_LIBSASL)
sasl_client_done();
+#elif defined(HAVE_LIBGSASL)
+ gsasl_done( Ctx );
+ Ctx = NULL;
#endif
}
@@ -196,6 +315,9 @@ imap_auth_free_client( imap_auth_client_t *client )
#if defined(HAVE_LIBSASL)
sasl_conn_t *session = client->session;
sasl_dispose( &session );
+#elif defined(HAVE_LIBGSASL)
+ Gsasl_session *session = client->session;
+ gsasl_finish( session );
#endif
}
@@ -355,6 +477,45 @@ attempt_sasl_auth( imap_auth_client_t *client, const char
*mechs )
&cmd, enc ? "AUTHENTICATE %s %s" : "AUTHENTICATE %s",
mech, enc );
free( enc );
return cmd;
+#elif defined(HAVE_LIBGSASL)
+ char *cmd = NULL;
+ char *mech = choose_sasl_mech( mechs );
+ if (!mech) {
+ info( "NOT using available SASL mechanism(s): %s\n",
AvailableMechs );
+ goto out;
+ }
+ info( "Authenticating with SASL mechanism %s...\n", mech );
+
+ Gsasl_session *session = NULL;
+ Gsasl_rc rc = gsasl_client_start( Ctx, mech, &session );
+ if (rc != GSASL_OK) {
+ const char *err = gsasl_strerror( rc );
+ error( "Error initializing SASL client: %s\n", err );
+ goto out;
+ }
+
+ client->session = session;
+ gsasl_session_hook_set( session, client );
+
+ if (client->saslir) {
+ const char *in = NULL;
+ char *out;
+ int cont = process_sasl_step( session, in, &out );
+ if (cont < 0)
+ goto out;
+ client->cont = cont;
+
+ const char *enc = (out && *out) ? out : "=";
+ nfasprintf( &cmd, "AUTHENTICATE %s %s", mech, enc );
+ gsasl_free( out );
+ } else {
+ client->cont = 1;
+ nfasprintf( &cmd, "AUTHENTICATE %s", mech );
+ }
+
+ out:
+ free( mech );
+ return cmd;
#else
(void)client;
(void)mechs;
@@ -459,6 +620,26 @@ imap_auth_cont(
*resp = enc;
*len = enc_len;
return 0;
+#elif defined(HAVE_LIBGSASL)
+ Gsasl_session *session = client->session;
+ char *out;
+ int cont = process_sasl_step( session, prompt, &out );
+ if (cont < 0)
+ return -1;
+ client->cont = cont;
+
+# if defined(_WIN32) || defined(__CYGWIN__)
+ if (out) {
+ // Move response to application heap (legacy Windows thing)
+ char *cpy = nfstrdup( out );
+ gsasl_free( out );
+ out = cpy;
+ }
+# endif
+
+ *resp = out;
+ *len = out ? strlen( out ) : 0;
+ return 0;
#else
(void)prompt;
(void)resp;
@@ -494,5 +675,23 @@ imap_auth_done( imap_auth_client_t *client, int ok )
"Warning: SASL wants more steps despite "
"successful IMAP authentication. Ignoring...\n"
);
}
+#elif defined(HAVE_LIBGSASL)
+ Gsasl_session *session = client->session;
+ const char *in = NULL;
+ char *out;
+ int cont = process_sasl_step( session, in, &out );
+ if (cont < 0) {
+ warn(
+ "Warning: SASL reported failure despite "
+ "successful IMAP authentication. Ignoring...\n"
);
+ } else {
+ client->cont = cont;
+ if (out && *out) {
+ warn(
+ "Warning: SASL wants more steps despite
"
+ "successful IMAP authentication.
Ignoring...\n" );
+ }
+ gsasl_free( out );
+ }
#endif
}
diff --git a/src/imap_auth.h b/src/imap_auth.h
index 2e62a0be041e..60b8882b11e5 100644
--- a/src/imap_auth.h
+++ b/src/imap_auth.h
@@ -37,6 +37,7 @@ typedef struct imap_auth_client {
* Library-dependent session state.
* Currently holds:
* - Cyrus SASL: sasl_conn_t *
+ * - GNU SASL: Gsasl_session *
* - No library: NULL
*/
void *session;
diff --git a/src/main.c b/src/main.c
index 8581b0cf6c72..6744cd5594a8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -67,8 +67,10 @@ PACKAGE " " VERSION " - mailbox synchronizer\n"
#endif
#ifdef HAVE_LIBSASL
" +HAVE_LIBSASL"
+#elif defined(HAVE_LIBGSASL)
+" +HAVE_LIBGSASL"
#else
-" -HAVE_LIBSASL"
+" -HAVE_LIB(G)SASL"
#endif
#ifdef HAVE_LIBZ
" +HAVE_LIBZ"
--
2.47.3
_______________________________________________
isync-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/isync-devel