Oswald Buddenhagen <[email protected]> writes:
> On Wed, Jun 25, 2014 at 02:28:42PM +0200, Jan Synacek wrote:
>> Oswald Buddenhagen <[email protected]> writes:
>> > note that there is also a sasl branch in git. you can take a lot from
>> > the commit and also the review in the commit message.
>> > ideally, revive it as well.
>>
>> It looks like the sasl code does slightly more than mine, and is
>> simpler. Reviving the branch makes sense to me.
>>
>> Just to be sure. Do you prefer the sasl implementation, or pure GSSAPI?
>>
> if i read it right, SASL is basically a PAM equivalent for network auth,
> so it's more generic than interfacing directly with gssapi, and thus
> lowers the overall cost.
> unless you think it would come with some significant downsides over
> gssapi (depedencies?), it probably makes sense to go a sasl-only route.
> failing that, it may make sense to have sasl ifdefs, and specific other
> ifdefs (gssapi and cram-md5 so far) in the else branches.
> convince me either way. :)
Here's second version of my patch. I decided to kind of revive (I
cleaned it up a lot) the original sasl branch. Since the SASL support
will already be there, I don't think it makes sense to have additional
code for GSSAPI only.
As the original author, I tested only GSSAPI and PLAIN authentication
mechanisms.
The existing CRAM authentication code could be crammed into the SASL
code as well. It shouldn't require almost any SASL code changes, but
that's for another patch.
--
Jan Synacek
Software Engineer, Red Hat
>From a804fe1da8ae9962b5db3bc60dc97f658d278eee Mon Sep 17 00:00:00 2001
From: Jan Synacek <[email protected]>
Date: Thu, 26 Jun 2014 13:00:18 +0200
Subject: [PATCH] implement sasl support
---
configure.ac | 49 +++++++++++++++
src/Makefile.am | 2 +-
src/common.h | 2 +
src/drv_imap.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
src/socket.c | 2 -
src/socket.h | 3 +
6 files changed, 238 insertions(+), 10 deletions(-)
diff --git a/configure.ac b/configure.ac
index 3a9927d..27b8c0b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -100,6 +100,45 @@ if test "x$ob_cv_with_ssl" != xno; then
fi
AC_SUBST(SSL_LIBS)
+have_sasl_paths=
+AC_ARG_WITH(sasl,
+ AS_HELP_STRING([--with-sasl[=PATH]], [where to look for SASL [detect]]),
+ [ob_cv_with_sasl=$withval])
+if test "x$ob_cv_with_sasl" != xno; then
+ case $ob_cv_with_sasl in
+ ""|yes)
+ dnl Try various possible paths here...
+ ;;
+ *)
+ SASL_LDFLAGS=-L$ob_cv_with_sasl/lib$libsuff
+ SASL_CPPFLAGS=-I$ob_cv_with_sasl/include
+ ;;
+ esac
+ if test -z "$have_sasl_paths"; then
+ sav_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS $SASL_LDFLAGS"
+ AC_CHECK_LIB(sasl2, sasl_client_init,
+ [SASL_LIBS="-lsasl2" have_sasl_paths=yes])
+ LDFLAGS=$sav_LDFLAGS
+ fi
+
+ sav_CPPFLAGS=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS $SASL_CPPFLAGS"
+ AC_CHECK_HEADER(sasl/sasl.h, , [have_sasl_paths=])
+ CPPFLAGS=$sav_CPPFLAGS
+
+ 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])
+ fi
+ else
+ AC_DEFINE(HAVE_LIBSASL, 1, [if you have the SASL libraries])
+ CPPFLAGS="$CPPFLAGS $SASL_CPPFLAGS"
+ LDFLAGS="$LDFLAGS $SASL_LDFLAGS"
+ fi
+fi
+AC_SUBST(SASL_LIBS)
+
AC_CACHE_CHECK([for Berkley DB >= 4.2], ac_cv_berkdb4,
[ac_cv_berkdb4=no
AC_TRY_LINK([#include <db.h>],
@@ -131,3 +170,13 @@ else
Not using SSL
])
fi
+
+if test -n "$have_sasl_paths"; then
+ AC_MSG_RESULT([
+Using SASL
+])
+else
+ AC_MSG_RESULT([
+Not using SASL
+])
+fi
diff --git a/src/Makefile.am b/src/Makefile.am
index 9cfd0af..85d5bab 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,7 +6,7 @@ SUBDIRS = $(compat_dir)
bin_PROGRAMS = mbsync mdconvert
mbsync_SOURCES = main.c sync.c config.c util.c socket.c driver.c drv_imap.c drv_maildir.c
-mbsync_LDADD = -ldb $(SSL_LIBS) $(SOCK_LIBS)
+mbsync_LDADD = -ldb $(SSL_LIBS) $(SOCK_LIBS) $(SASL_LIBS)
noinst_HEADERS = common.h config.h driver.h sync.h socket.h
mdconvert_SOURCES = mdconvert.c
diff --git a/src/common.h b/src/common.h
index fb868b3..5cf77f2 100644
--- a/src/common.h
+++ b/src/common.h
@@ -133,4 +133,6 @@ void fake_fd( int fd, int events );
void del_fd( int fd );
void main_loop( void );
+#define ENCODED_SIZE(n) (4*((n+2)/3))
+
#endif
diff --git a/src/drv_imap.c b/src/drv_imap.c
index adacc43..fa877c9 100644
--- a/src/drv_imap.c
+++ b/src/drv_imap.c
@@ -36,6 +36,11 @@
#include <time.h>
#include <sys/wait.h>
+#if HAVE_LIBSASL
+# include <sasl/sasl.h>
+# include <sasl/saslutil.h>
+#endif
+
typedef struct imap_server_conf {
struct imap_server_conf *next;
char *name;
@@ -109,6 +114,9 @@ typedef struct imap_store {
void (*imap_cancel)( void *aux );
} callbacks;
void *callback_aux;
+#ifdef HAVE_LIBSASL
+ sasl_conn_t *sasl;
+#endif
conn_t conn; /* this is BIG, so put it last */
} imap_store_t;
@@ -167,6 +175,10 @@ struct imap_cmd_refcounted {
enum CAPABILITY {
NOLOGIN = 0,
+#ifdef HAVE_LIBSASL
+ PLAIN,
+ GSSAPI,
+#endif
#ifdef HAVE_LIBSSL
CRAM,
STARTTLS,
@@ -179,6 +191,10 @@ enum CAPABILITY {
static const char *cap_list[] = {
"LOGINDISABLED",
+#ifdef HAVE_LIBSASL
+ "AUTH=PLAIN"
+ "AUTH=GSSAPI"
+#endif
#ifdef HAVE_LIBSSL
"AUTH=CRAM-MD5",
"STARTTLS",
@@ -1629,6 +1645,122 @@ imap_open_store_authenticate_p3( imap_store_t *ctx, struct imap_cmd *cmd ATTR_UN
}
#endif
+#if HAVE_LIBSASL
+
+static char *
+prompt_for_password( char *account )
+{
+ char prompt[80];
+ char *arg;
+
+ sprintf( prompt, "Password (%s): ", account );
+ arg = getpass( prompt );
+ if (!arg) {
+ perror( "getpass" );
+ exit( 1 );
+ }
+
+ return arg;
+}
+
+static sasl_callback_t sasl_callbacks[] = {
+ {SASL_CB_USER, NULL, NULL},
+ {SASL_CB_AUTHNAME, NULL, NULL},
+ {SASL_CB_PASS, NULL, NULL},
+ {SASL_CB_LIST_END, NULL, NULL}
+};
+
+static void process_sasl_interact( sasl_interact_t *interact, imap_server_conf_t *srvc )
+{
+ while (interact->id) {
+ switch (interact->id) {
+ case SASL_CB_USER:
+ case SASL_CB_AUTHNAME:
+ interact->result = srvc->user;
+ interact->len = strlen( srvc->user );
+ break;
+ case SASL_CB_PASS:
+ if (!srvc->pass)
+ srvc->pass = nfstrdup( prompt_for_password( srvc->name ) );
+ interact->result = srvc->pass;
+ interact->len = strlen( srvc->pass );
+ break;
+ default:
+ sys_error( "Unknown SASL interaction ID\n" );
+ }
+ ++interact;
+ }
+}
+
+static int
+do_sasl_auth ( imap_store_t *ctx, struct imap_cmd *cmdp, const char *prompt )
+{
+ imap_server_conf_t *srvc = ((imap_store_conf_t *)ctx->gen.conf)->server;
+ int rc;
+ unsigned int prompt_len = 0, out_len, enc_len = 0;
+ char *out, *enc = NULL;
+ sasl_interact_t *interact = NULL;
+
+ cmdp->param.cont = NULL;
+
+ if(prompt) {
+ prompt_len = strlen( prompt );
+ /* We're decoding, the output will be shorter than prompt_len. */
+ enc = malloc( prompt_len );
+ rc = sasl_decode64( prompt, prompt_len, enc, prompt_len, &enc_len );
+ if(rc != SASL_OK) {
+ error( "Couldn't decode buffer" );
+ rc = -1;
+ goto out;
+ }
+ }
+
+ do {
+ rc = sasl_client_step( ctx->sasl, enc, enc_len, &interact, (const char **)&out, &out_len );
+ if (rc == SASL_INTERACT)
+ process_sasl_interact( interact, srvc );
+ } while (rc == SASL_INTERACT);
+
+ if (rc != SASL_OK && rc != SASL_CONTINUE) {
+ sys_error( "%s\n", sasl_errdetail( ctx->sasl ) );
+ rc = -1;
+ goto out;
+ }
+ if (rc == SASL_CONTINUE) {
+ cmdp->param.cont = do_sasl_auth;
+ }
+
+ if (enc) {
+ free( enc );
+ }
+ enc = nfmalloc( ENCODED_SIZE( out_len ) + 1 );
+ rc = sasl_encode64( out, out_len, enc, ENCODED_SIZE( out_len ) + 1, &enc_len );
+ if(rc != SASL_OK) {
+ error( "Couldn't encode buffer" );
+ rc = -1;
+ goto out;
+ }
+
+ if (out_len <= 0) {
+ rc = 0;
+ goto out;
+ }
+
+ if (DFlags & VERBOSE)
+ info ( ">+> %s\n", enc );
+
+ if (socket_write( &ctx->conn, enc, enc_len, GiveOwn ) < 0) {
+ rc = -1;
+ goto out;
+ }
+ return socket_write( &ctx->conn, "\r\n", 2, KeepOwn );
+out:
+ if ( enc )
+ free( enc );
+ return rc;
+}
+#endif
+
static void
imap_open_store_authenticate2( imap_store_t *ctx )
{
@@ -1641,6 +1773,49 @@ imap_open_store_authenticate2( imap_store_t *ctx )
error( "Skipping account %s, no user\n", srvc->name );
goto bail;
}
+#ifdef HAVE_LIBSASL
+ if (srvc->sconf.use_sasl) {
+ int rc;
+ char *mech = NULL;
+ sasl_interact_t *interact = NULL;
+ struct imap_cmd *cmd;
+
+ info( "Authenticating with SASL\n" );
+
+ rc = sasl_client_init( sasl_callbacks );
+ if (rc != SASL_OK) {
+ sys_error( "SASL client initialization failed\n" );
+ goto bail;
+ }
+
+ rc = sasl_client_new( "imap", srvc->sconf.host, NULL, NULL, NULL, 0, &ctx->sasl );
+ if (rc != SASL_OK) {
+ sys_error( "%s\n", sasl_errdetail( ctx->sasl ) );
+ goto bail;
+ }
+
+ do {
+ /* Just supply all client-supported mechs and let SASL sort it out. */
+ rc = sasl_client_start( ctx->sasl, "GSSAPI PLAIN", &interact,
+ NULL, NULL, (const char **)&mech );
+ if (rc == SASL_INTERACT)
+ process_sasl_interact( interact, srvc );
+ } while (rc == SASL_INTERACT);
+
+ if (rc != SASL_OK && rc != SASL_CONTINUE) {
+ sys_error( "%s\n", sasl_errdetail( ctx->sasl ) );
+ goto bail;
+ }
+
+ cmd = new_imap_cmd( sizeof(*cmd) );
+ cmd->param.cont = do_sasl_auth;
+
+ if (DFlags & VERBOSE)
+ info( "Using \"%s\" SASL mechanism\n", mech );
+ imap_exec( ctx, cmd, imap_open_store_authenticate2_p2, "AUTHENTICATE %s", mech );
+ return;
+ }
+#endif
if (srvc->pass_cmd) {
FILE *fp;
int ret;
@@ -1670,13 +1845,7 @@ imap_open_store_authenticate2( imap_store_t *ctx )
free( srvc->pass ); /* From previous runs */
srvc->pass = nfstrdup( buffer );
} else if (!srvc->pass) {
- char prompt[80];
- sprintf( prompt, "Password (%s): ", srvc->name );
- arg = getpass( prompt );
- if (!arg) {
- perror( "getpass" );
- exit( 1 );
- }
+ arg = prompt_for_password( srvc->name );
if (!*arg) {
error( "Skipping account %s, no password\n", srvc->name );
goto bail;
@@ -2246,6 +2415,9 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
server->require_ssl = 1;
server->sconf.use_tlsv1 = 1;
#endif
+#ifdef HAVE_LIBSASL
+ server->sconf.use_sasl = 1;
+#endif
server->max_in_progress = INT_MAX;
while (getcline( cfg ) && cfg->cmd) {
@@ -2306,6 +2478,10 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
else if (!strcasecmp( "RequireCRAM", cfg->cmd ))
server->require_cram = parse_bool( cfg );
#endif
+#ifdef HAVE_LIBSASL
+ else if (!strcasecmp( "UseSASL", cfg->cmd ))
+ server->sconf.use_sasl = parse_bool( cfg );
+#endif
else if (!strcasecmp( "Tunnel", cfg->cmd ))
server->sconf.tunnel = nfstrdup( cfg->val );
else if (store) {
diff --git a/src/socket.c b/src/socket.c
index f47e375..f134841 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -739,8 +739,6 @@ socket_fd_cb( int events, void *aux )
#ifdef HAVE_LIBSSL
/* this isn't strictly socket code, but let's have all OpenSSL use in one file. */
-#define ENCODED_SIZE(n) (4*((n+2)/3))
-
static char
hexchar( unsigned int b )
{
diff --git a/src/socket.h b/src/socket.h
index 1545b39..2388007 100644
--- a/src/socket.h
+++ b/src/socket.h
@@ -32,6 +32,9 @@ typedef struct server_conf {
char *tunnel;
char *host;
int port;
+#ifdef HAVE_LIBSASL
+ char use_sasl;
+#endif
#ifdef HAVE_LIBSSL
char *cert_file;
char use_imaps;
--
1.9.3
------------------------------------------------------------------------------
Open source business process management suite built on Java and Eclipse
Turn processes into business applications with Bonita BPM Community Edition
Quickly connect people, data, and systems into organized workflows
Winner of BOSSIE, CODIE, OW2 and Gartner awards
http://p.sf.net/sfu/Bonitasoft
_______________________________________________
isync-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/isync-devel