Hello,
I've implemented GSSAPI support for isync. I was a bit unsure about the
ifdefs and how to structure them, since I use EVP_DecodeBlock() to
decode base64, which means dependency on OpenSSL. Consider this patch
version 1.
Thanks,
--
Jan Synacek
Software Engineer, Red Hat
>From 6677e6f4b7ab436362b2a0b2ff623d516d15c3ab Mon Sep 17 00:00:00 2001
From: Jan Synacek <jsyna...@redhat.com>
Date: Fri, 13 Jun 2014 09:22:05 +0200
Subject: [PATCH] implement gssapi support
---
configure.ac | 14 ++++
src/common.h | 4 ++
src/drv_imap.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/socket.h | 3 +
src/util.c | 20 ++++++
5 files changed, 242 insertions(+)
diff --git a/configure.ac b/configure.ac
index 3a9927d..bf68daf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -100,6 +100,20 @@ if test "x$ob_cv_with_ssl" != xno; then
fi
AC_SUBST(SSL_LIBS)
+AC_ARG_ENABLE([gssapi],
+ [AC_HELP_STRING([--enable-gssapi],
+ [enable gssapi support [no]]
+ )],
+ [gssapi=${enableval}],
+ [gssapi=no])
+if test "${gssapi}" = yes; then
+ AC_CHECK_LIB(gssapi_krb5, gss_init_sec_context, [gssapi_krb5=yes])
+ if test "${gssapi_krb5}" = yes; then
+ AC_DEFINE(HAVE_LIBGSSAPI, 1, [if you have the gssapi_krb5 library])
+ LDFLAGS="$LDFLAGS -lgssapi_krb5"
+ fi
+fi
+
AC_CACHE_CHECK([for Berkley DB >= 4.2], ac_cv_berkdb4,
[ac_cv_berkdb4=no
AC_TRY_LINK([#include <db.h>],
diff --git a/src/common.h b/src/common.h
index fb868b3..6afcc9f 100644
--- a/src/common.h
+++ b/src/common.h
@@ -133,4 +133,8 @@ void fake_fd( int fd, int events );
void del_fd( int fd );
void main_loop( void );
+#if HAVE_LIBSSL && HAVE_LIBGSSAPI
+int from_base64( char *, const char *, const int );
+#endif
+
#endif
diff --git a/src/drv_imap.c b/src/drv_imap.c
index adacc43..9c08934 100644
--- a/src/drv_imap.c
+++ b/src/drv_imap.c
@@ -35,6 +35,11 @@
#include <ctype.h>
#include <time.h>
#include <sys/wait.h>
+#if HAVE_LIBSSL && HAVE_LIBGSSAPI
+# include <gssapi.h>
+# include <openssl/evp.h>
+#endif
+
typedef struct imap_server_conf {
struct imap_server_conf *next;
@@ -180,6 +185,9 @@ enum CAPABILITY {
static const char *cap_list[] = {
"LOGINDISABLED",
#ifdef HAVE_LIBSSL
+# ifdef HAVE_LIBGSSAPI
+ "AUTH=GSSAPI",
+# endif
"AUTH=CRAM-MD5",
"STARTTLS",
#endif
@@ -1442,6 +1450,182 @@ do_cram_auth( imap_store_t *ctx, struct imap_cmd *cmdp,
const char *prompt )
}
#endif
+#ifdef HAVE_LIBGSSAPI
+static gss_ctx_id_t Gss_ctx;
+static gss_name_t Gss_name;
+
+#define GSS_BUFSIZE 16384
+
+static int do_gssapi_auth_p2 ( imap_store_t *, struct imap_cmd *, const char *
);
+static int do_gssapi_auth_p3 ( imap_store_t *, struct imap_cmd *, const char *
);
+
+static int
+send_gss_buffer( imap_store_t *ctx, gss_buffer_t buffer )
+{
+ char *buf;
+ int len;
+
+ buf = nfcalloc( GSS_BUFSIZE );
+ len = EVP_EncodeBlock( (unsigned char *)buf, (unsigned char
*)buffer->value, buffer->length );
+
+ if (socket_write( &ctx->conn, buf, len, GiveOwn ) < 0)
+ return -1;
+ return socket_write( &ctx->conn, "\r\n", 2, KeepOwn );
+}
+
+static int
+do_gssapi_auth( imap_store_t *ctx, struct imap_cmd *cmdp, const char *prompt
ATTR_UNUSED )
+{
+ imap_server_conf_t *srvc = ((imap_store_conf_t *)ctx->gen.conf)->server;
+
+ gss_buffer_desc input;
+ gss_buffer_desc output;
+ OM_uint32 maj, min;
+ char buf[GSS_BUFSIZE];
+
+ cmdp->param.cont = do_gssapi_auth_p2;
+
+ snprintf( buf, sizeof( buf ), "imap@%s", srvc->sconf.host );
+ input.value = buf;
+ input.length = strlen( buf ) + 1;
+
+ maj = gss_import_name( &min, &input, GSS_C_NT_HOSTBASED_SERVICE,
&Gss_name );
+ if (maj != GSS_S_COMPLETE) {
+ error( "Couldn't get service name for [%s]\n", buf );
+ return -1;
+ }
+
+ if (DFlags & VERBOSE) {
+ gss_OID mech_name;
+ maj = gss_display_name( &min, Gss_name, &input, &mech_name );
+ printf( "Using service name [%s]\n", (char *)input.value );
+ fflush( stdout );
+ }
+
+ input.value = NULL; input.length = 0;
+
+ maj = gss_init_sec_context(
+ &min,
+ GSS_C_NO_CREDENTIAL,
+ &Gss_ctx,
+ Gss_name,
+ GSS_C_NO_OID,
+ GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
+ 0,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &input,
+ NULL,
+ &output,
+ NULL,
+ NULL );
+
+ if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) {
+ error( "Error acquiring credentials. Do you have a kerberos
ticket?\n" );
+ gss_release_name( &min, &Gss_name );
+ return -1;
+ }
+
+ return send_gss_buffer( ctx, &output );
+}
+
+static int
+do_gssapi_auth_p2( imap_store_t *ctx, struct imap_cmd *cmdp, const char
*prompt )
+{
+ gss_buffer_desc input;
+ gss_buffer_desc output;
+ OM_uint32 maj, min;
+ char *buf;
+
+ cmdp->param.cont = do_gssapi_auth_p3;
+
+ buf = nfcalloc( GSS_BUFSIZE );
+ input.length = from_base64( buf, prompt, strlen( prompt ) );
+ input.value = buf;
+
+ maj = gss_init_sec_context(
+ &min,
+ GSS_C_NO_CREDENTIAL,
+ &Gss_ctx,
+ Gss_name,
+ GSS_C_NO_OID,
+ GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
+ 0,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &input,
+ NULL,
+ &output,
+ NULL,
+ NULL );
+
+ if (maj == GSS_S_CONTINUE_NEEDED)
+ cmdp->param.cont = do_gssapi_auth_p2;
+ else
+ gss_release_name( &min, &Gss_name );
+
+ free( buf );
+
+ if (output.length > 0)
+ return send_gss_buffer( ctx, &output );
+
+ return 0;
+}
+
+static int
+do_gssapi_auth_p3( imap_store_t *ctx, struct imap_cmd *cmdp, const char
*prompt )
+{
+ imap_server_conf_t *srvc = ((imap_store_conf_t *)ctx->gen.conf)->server;
+ gss_buffer_desc input;
+ gss_buffer_desc output;
+ OM_uint32 maj, min;
+ char *buf;
+ int plen;
+
+ cmdp->param.cont = 0;
+
+ plen = strlen( prompt );
+ buf = nfcalloc( plen + 1 );
+ input.length = from_base64( buf, prompt, plen );
+ input.value = buf;
+
+ maj = gss_unwrap( &min,
+ Gss_ctx,
+ &input,
+ &output,
+ NULL,
+ NULL );
+
+ if (maj != GSS_S_COMPLETE) {
+ error( "Unexpected error while calling gss_unwrap()\n" );
+ return -1;
+ }
+
+ /* First octet means supported security level, second through forth
+ octets mean maximum token size. We don't care about setting any, so
+ send them back as they came in. And append the username after them.
*/
+ memcpy( buf, output.value, 4 );
+ strcpy( buf + 4, srvc->user );
+ input.value = buf;
+ input.length = 4 + strlen( srvc->user );
+
+ maj = gss_wrap( &min,
+ Gss_ctx,
+ 0,
+ GSS_C_QOP_DEFAULT,
+ &input,
+ NULL,
+ &output );
+
+ gss_release_buffer( &min, &input );
+
+ if (maj != GSS_S_COMPLETE) {
+ error( "Unexpected error while calling gss_wrap()\n" );
+ return -1;
+ }
+
+ return send_gss_buffer( ctx, &output );
+}
+#endif
+
static void imap_open_store_connected( int, void * );
#ifdef HAVE_LIBSSL
static void imap_open_store_tlsstarted1( int, void * );
@@ -1641,6 +1825,16 @@ imap_open_store_authenticate2( imap_store_t *ctx )
error( "Skipping account %s, no user\n", srvc->name );
goto bail;
}
+#ifdef HAVE_LIBGSSAPI
+ if (srvc->sconf.use_gssapi) {
+ struct imap_cmd *cmd = new_imap_cmd( sizeof(*cmd) );
+
+ info( "Authenticating with GSSAPI\n" );
+ cmd->param.cont = do_gssapi_auth;
+ imap_exec( ctx, cmd, imap_open_store_authenticate2_p2,
"AUTHENTICATE GSSAPI" );
+ return;
+ }
+#endif
if (srvc->pass_cmd) {
FILE *fp;
int ret;
@@ -2245,6 +2439,9 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
*/
server->require_ssl = 1;
server->sconf.use_tlsv1 = 1;
+# ifdef HAVE_LIBGSSAPI
+ server->sconf.use_gssapi = 0;
+# endif
#endif
server->max_in_progress = INT_MAX;
@@ -2306,6 +2503,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_LIBGSSAPI
+ else if (!strcasecmp( "UseGSSAPI", cfg->cmd ))
+ server->sconf.use_gssapi = parse_bool( cfg );
+#endif
else if (!strcasecmp( "Tunnel", cfg->cmd ))
server->sconf.tunnel = nfstrdup( cfg->val );
else if (store) {
diff --git a/src/socket.h b/src/socket.h
index 1545b39..1598a0e 100644
--- a/src/socket.h
+++ b/src/socket.h
@@ -41,6 +41,9 @@ typedef struct server_conf {
char ssl_ctx_valid;
unsigned num_trusted;
SSL_CTX *SSLContext;
+# ifdef HAVE_LIBGSSAPI
+ char use_gssapi;
+# endif
#endif
} server_conf_t;
diff --git a/src/util.c b/src/util.c
index c76916b..12a1073 100644
--- a/src/util.c
+++ b/src/util.c
@@ -713,6 +713,26 @@ event_wait( void )
#endif
}
+#if HAVE_LIBSSL && HAVE_LIBGSSAPI
+#include <openssl/evp.h>
+
+int from_base64( char *out, const char *in, const int inlen )
+{
+ int len;
+
+ len = EVP_DecodeBlock( (unsigned char *)out, (unsigned char *)in, inlen
);
+ if (inlen > 2) {
+ if (in[inlen - 1] == '=') {
+ len--;
+ if (in[inlen - 2] == '=')
+ len--;
+ }
+ }
+
+ return len;
+}
+#endif
+
void
main_loop( void )
{
--
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
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel