Commit 0ad2b164 authored by Thomas Roessler's avatar Thomas Roessler

Inclue GSS authentization for IMAP.

parent 274bd021
......@@ -37,8 +37,8 @@ mutt_SOURCES = $(BUILT_SOURCES) \
status.c system.c thread.c charset.c history.c lib.c \
muttlib.c
mutt_LDADD = @[email protected] @[email protected] $(LIBIMAP) $(INTLLIBS)
mutt_DEPENDENCIES = @[email protected] @[email protected] $(INTLDEPS)
mutt_LDADD = @[email protected] @[email protected] $(LIBIMAP) $(GSSLIBS) $(INTLLIBS)
mutt_DEPENDENCIES = @[email protected] @[email protected] $(LIBIMAPDEPS) $(INTLDEPS)
DEFS=-DSHAREDIR=\"$(sharedir)\" -DSYSCONFDIR=\"$(sysconfdir)\" \
-DBINDIR=\"$(bindir)\" -DHAVE_CONFIG_H=1
......
......@@ -48,6 +48,9 @@
/* Do you want support for the IMAP protocol? (--enable-imap) */
#undef USE_IMAP
/* Do you want support for IMAP GSSAPI authentication? (--with-gss) */
#undef USE_GSS
/*
* Is mail spooled to the user's home directory? If defined, MAILPATH should
* be set to the filename of the spool mailbox relative the the home
......
......@@ -168,6 +168,11 @@ folder_format_str (char *dest, size_t destlen, char op, const char *src,
break;
case 'f':
#ifdef USE_IMAP
if (mx_is_imap (folder->ff->name))
strfcpy (fn, folder->ff->desc, sizeof (fn));
else
#endif
strfcpy (fn, folder->ff->name, sizeof(fn));
if (folder->ff->st != NULL)
{
......@@ -199,10 +204,10 @@ folder_format_str (char *dest, size_t destlen, char op, const char *src,
else
{
#ifdef USE_IMAP
if (strchr(folder->ff->name, '{'))
if (mx_is_imap (folder->ff->name))
{
snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
snprintf (dest, destlen, tmp, "IMAP");
snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
snprintf (dest, destlen, tmp, "IMAP");
}
#endif
}
......
......@@ -452,11 +452,33 @@ AC_ARG_ENABLE(imap, [ --enable-imap Enable IMAP support],
AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt))
AC_CHECK_FUNC(gethostent, , AC_CHECK_LIB(nsl, gethostent))
LIBIMAP="-Limap -limap"
LIBIMAPDEPS="\$(srcdir)/imap/imap.h \$(srcdir)/imap/libimap.a"
CPPFLAGS="$CPPFLAGS -I\$(srcdir)/imap"
need_imap="yes"
])
AM_CONDITIONAL(BUILD_IMAP, test x$need_imap = xyes)
AC_ARG_WITH(gss, [ --with-gss[=DIR] Compile in GSSAPI authentication for IMAP],
[
if test "$with_gss" != "no"
then
if test "$with_gss" != "yes"
then
CPPFLAGS="$CPPFLAGS -I$with_gss/include"
LDFLAGS="$LDFLAGS -L$with_gss/lib"
fi
saved_LIBS="$LIBS"
AC_CHECK_LIB(krb5, krb5_init_context,,
AC_MSG_ERROR([could not find libkrb5 which is needed for GSS authentication]))
AC_CHECK_LIB(gssapi_krb5, gss_init_sec_context,,
AC_MSG_ERROR([could not find libgssapi_krb5 which is needed for GSS authentication]), -lkrb5)
LIBS="$saved_LIBS"
GSSLIBS="-lgssapi_krb5 -lkrb5"
AC_DEFINE(USE_GSS)
fi
])
AC_ARG_ENABLE(debug, [ --enable-debug Enable debugging support],
[ AC_DEFINE(DEBUG) ])
......@@ -510,6 +532,8 @@ AC_ARG_ENABLE(exact-address, [ --enable-exact-address enable regeneration o
AC_SUBST(MUTT_LIB_OBJECTS)
AC_SUBST(LIBIMAP)
AC_SUBST(LIBIMAPDEPS)
AC_SUBST(GSSLIBS)
MUTT_AM_GNU_GETTEXT
CPPFLAGS="$CPPFLAGS -I\$(srcdir)/intl"
......
......@@ -27,11 +27,26 @@
#define MD5_BLOCK_LEN 64
#define MD5_DIGEST_LEN 16
#ifdef USE_GSS
#include <netinet/in.h>
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_generic.h>
#define GSS_BUFSIZE 8192
#define GSS_AUTH_P_NONE 1
#define GSS_AUTH_P_INTEGRITY 2
#define GSS_AUTH_P_PRIVACY 4
#endif
/* forward declarations */
static void hmac_md5 (const char* password, char* challenge,
unsigned char* response);
static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user,
const char* pass);
#ifdef USE_GSS
static int imap_auth_gss (IMAP_DATA* idata, const char* user);
#endif
/* hmac_md5: produce CRAM-MD5 challenge response. */
static void hmac_md5 (const char* password, char* challenge,
......@@ -84,6 +99,215 @@ static void hmac_md5 (const char* password, char* challenge,
MD5Final (response, &ctx);
}
#ifdef USE_GSS
/* imap_auth_gss: AUTH=GSSAPI support. Used unconditionally if the server
* supports it */
static int imap_auth_gss (IMAP_DATA* idata, const char* user)
{
gss_buffer_desc request_buf, send_token;
gss_buffer_t sec_token;
gss_name_t target_name;
gss_ctx_id_t context;
gss_OID mech_name;
gss_qop_t quality;
int cflags;
OM_uint32 maj_stat, min_stat;
char buf1[GSS_BUFSIZE], buf2[GSS_BUFSIZE], server_conf_flags;
unsigned long buf_size;
char seq[16];
dprint (2, (debugfile, "Attempting GSS login...\n"));
/* get an IMAP service ticket for the server */
snprintf (buf1, sizeof (buf1), "[email protected]%s", idata->conn->server);
request_buf.value = buf1;
request_buf.length = strlen (buf1) + 1;
maj_stat = gss_import_name (&min_stat, &request_buf, gss_nt_service_name,
&target_name);
if (maj_stat != GSS_S_COMPLETE)
{
dprint (2, (debugfile, "Couldn't get service name for [%s]\n", buf1));
return -1;
}
else if (debuglevel >= 2)
{
maj_stat = gss_display_name (&min_stat, target_name, &request_buf,
&mech_name);
dprint (2, (debugfile, "Using service name [%s]\n",
(char*) request_buf.value));
maj_stat = gss_release_buffer (&min_stat, &request_buf);
}
/* now begin login */
mutt_message _("Authenticating (GSSAPI)...");
imap_make_sequence (seq, sizeof (seq));
snprintf (buf1, sizeof (buf1), "%s AUTHENTICATE GSSAPI\r\n", seq);
mutt_socket_write (idata->conn, buf1);
/* expect a null continuation response ("+") */
if (mutt_socket_read_line_d (buf1, sizeof (buf1), idata->conn) < 0)
{
dprint (1, (debugfile, "Error receiving server response.\n"));
gss_release_name (&min_stat, &target_name);
return -1;
}
if (buf1[0] != '+')
{
dprint (2, (debugfile, "Invalid response from server: %s\n", buf1));
gss_release_name (&min_stat, &target_name);
return -1;
}
/* now start the security context initialisation loop... */
dprint (2, (debugfile, "Sending credentials\n"));
sec_token = GSS_C_NO_BUFFER;
context = GSS_C_NO_CONTEXT;
do
{
/* build token */
maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &context,
target_name, NULL, 0, 0, NULL, sec_token, NULL, &send_token,
(unsigned int*) &cflags, NULL);
if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
{
dprint (1, (debugfile, "Error exchanging credentials\n"));
gss_release_name (&min_stat, &target_name);
/* end authentication attempt */
mutt_socket_write (idata->conn, "*\r\n");
mutt_socket_read_line_d (buf1, sizeof (buf1), idata->conn);
return -1;
}
/* send token */
mutt_to_base64 ((unsigned char*) buf1, send_token.value,
send_token.length);
gss_release_buffer (&min_stat, &send_token);
strcpy (buf1 + strlen (buf1), "\r\n");
mutt_socket_write (idata->conn, buf1);
if (maj_stat == GSS_S_CONTINUE_NEEDED)
{
if (mutt_socket_read_line_d (buf1, sizeof (buf1), idata->conn) < 0)
{
dprint (1, (debugfile, "Error receiving server response.\n"));
gss_release_name (&min_stat, &target_name);
return -1;
}
request_buf.length = mutt_from_base64 (buf2, buf1 + 2);
request_buf.value = buf2;
sec_token = &request_buf;
}
}
while (maj_stat == GSS_S_CONTINUE_NEEDED);
gss_release_name (&min_stat, &target_name);
/* get security flags and buffer size */
if (mutt_socket_read_line_d (buf1, sizeof (buf1), idata->conn) < 0)
{
dprint (1, (debugfile, "Error receiving server response.\n"));
return -1;
}
request_buf.length = mutt_from_base64 (buf2, buf1 + 2);
request_buf.value = buf2;
maj_stat = gss_unwrap (&min_stat, context, &request_buf, &send_token,
&cflags, &quality);
if (maj_stat != GSS_S_COMPLETE)
{
dprint (2, (debugfile, "Couldn't unwrap security level data\n"));
gss_release_buffer (&min_stat, &send_token);
return -1;
}
dprint (2, (debugfile, "Credential exchange complete\n"));
/* first octet is security levels supported. We want NONE */
server_conf_flags = ((char*) send_token.value)[0];
if ( !(((char*) send_token.value)[0] & GSS_AUTH_P_NONE) )
{
dprint (2, (debugfile, "Server requires integrity or privace\n"));
gss_release_buffer (&min_stat, &send_token);
return -1;
}
/* we don't care about buffer size if we don't wrap content. But here it is */
((char*) send_token.value)[0] = 0;
buf_size = ntohl (*((long *) send_token.value));
gss_release_buffer (&min_stat, &send_token);
dprint (2, (debugfile, "Unwrapped security level flags: %c%c%c\n",
server_conf_flags & GSS_AUTH_P_NONE ? 'N' : '-',
server_conf_flags & GSS_AUTH_P_INTEGRITY ? 'I' : '-',
server_conf_flags & GSS_AUTH_P_PRIVACY ? 'P' : '-'));
dprint (2, (debugfile, "Maximum GSS token size is %ld\n", buf_size));
/* agree to terms (hack!) */
buf_size = htonl (buf_size); /* not relevant without integrity/privacy */
memcpy (buf1, &buf_size, 4);
buf1[0] = GSS_AUTH_P_NONE;
/* server decides if principal can log in as user */
strncpy (buf1 + 4, user, sizeof (buf1) - 4);
request_buf.value = buf1;
request_buf.length = 4 + strlen (user) + 1;
maj_stat = gss_wrap (&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
&cflags, &send_token);
if (maj_stat != GSS_S_COMPLETE)
{
dprint (2, (debugfile, "Error creating login request\n"));
return -1;
}
mutt_to_base64 ((unsigned char*) buf1, send_token.value, send_token.length);
dprint (2, (debugfile, "Requesting authorisation as %s\n", user));
strncat (buf1, "\r\n", sizeof (buf1));
mutt_socket_write (idata->conn, buf1);
/* Joy of victory or agony of defeat? */
if (mutt_socket_read_line_d (buf1, GSS_BUFSIZE, idata->conn) < 0)
{
dprint (1, (debugfile, "Error receiving server response.\n"));
return -1;
}
if (imap_code (buf1))
{
/* flush the security context */
dprint (2, (debugfile, "Releasing GSS credentials\n"));
maj_stat = gss_delete_sec_context (&min_stat, &context, &send_token);
if (maj_stat != GSS_S_COMPLETE)
{
dprint (1, (debugfile, "Error releasing credentials\n"));
return -1;
}
/* send_token may contain a notification to the server to flush
* credentials. RFC 1731 doesn't specify what to do, and since this
* support is only for authentication, we'll assume the server knows
* enough to flush its own credentials */
gss_release_buffer (&min_stat, &send_token);
dprint (2, (debugfile, "GSS login complete\n"));
return 0;
}
/* logon failed */
dprint (2, (debugfile, "GSS login failed.\n"));
return -1;
}
#endif
/* imap_auth_cram_md5: AUTH=CRAM-MD5 support. Used unconditionally if the
* server supports it */
static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user,
......@@ -95,7 +319,7 @@ static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user,
char seq[16];
dprint (2, (debugfile, "Attempting CRAM-MD5 login...\n"));
mutt_message _("Logging in (CRAM-MD5)...");
mutt_message _("Authenticating (CRAM-MD5)...");
imap_make_sequence (seq, sizeof (seq));
snprintf (obuf, LONG_STRING, "%s AUTHENTICATE CRAM-MD5\r\n", seq);
mutt_socket_write (idata->conn, obuf);
......@@ -175,6 +399,8 @@ static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user,
/* imap_authenticate: loop until success or user abort. At each loop, all
* supported authentication methods are tried, from strongest to weakest.
* Currently available:
* GSSAPI: strongest available form, requires Kerberos V infrastructure,
* or possibly alternatively Heimdal.
* CRAM-MD5: like APOP or CHAP. Safe against replay and sniffing, but
* requires that your key be stored on the server, readable by the
* server account. UW-IMAP supports this method since at least 4.5, if
......@@ -183,9 +409,6 @@ static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user,
* exchange, furthermore uses unix login techniques so this same password
* can be used to log in to the server or others that share the
* credentials database.
* Pending:
* GSSAPI: strongest available form, requires Kerberos V infrastructure,
* or possibly alternatively Heimdal.
* Unavailable:
* KERBEROS_V4. Superceded by GSSAPI.
*/
......@@ -214,6 +437,22 @@ int imap_authenticate (IMAP_DATA *idata, CONNECTION *conn)
else
strfcpy (user, ImapUser, sizeof (user));
#ifdef USE_GSS
/* attempt GSSAPI authentication, if available */
if (mutt_bit_isset (idata->capabilities, AGSSAPI))
{
if ((r = imap_auth_gss (idata, user)))
{
mutt_error _("GSSAPI authentication failed.");
sleep (1);
}
else
return 0;
}
else
dprint (2, (debugfile, "GSSAPI authentication is not available\n"));
#endif
/* attempt CRAM-MD5 if available */
if (mutt_bit_isset (idata->capabilities, ACRAM_MD5))
{
......@@ -234,14 +473,14 @@ int imap_authenticate (IMAP_DATA *idata, CONNECTION *conn)
if ((r = imap_auth_cram_md5 (idata, user, ckey)))
{
mutt_error _("CRAM-MD5 login failed.");
mutt_error _("CRAM-MD5 authentication failed.");
sleep (1);
}
else
return 0;
}
else
dprint (2, (debugfile, "CRAM-MD5 authentication is not supported\n"));
dprint (2, (debugfile, "CRAM-MD5 authentication is not available\n"));
if (!ImapPass)
{
......
......@@ -321,8 +321,7 @@ static void imap_add_folder (char delim, char *folder, int noselect,
imap_qualify_path (tmp, sizeof (tmp), host, port, folder, NULL);
(state->entry)[state->entrylen].name = safe_strdup (tmp);
snprintf (tmp, sizeof (tmp), "IMAP %-25s %-25s", host, relpath);
(state->entry)[state->entrylen].desc = safe_strdup (tmp);
(state->entry)[state->entrylen].desc = safe_strdup (relpath);
(state->entry)[state->entrylen].notfolder = 0;
(state->entrylen)++;
......@@ -340,8 +339,7 @@ static void imap_add_folder (char delim, char *folder, int noselect,
if (strlen (relpath) < sizeof (relpath) - 2)
strcat (relpath, trailing_delim);
snprintf (tmp, sizeof (tmp), "IMAP %-25s %-25s", host, relpath);
(state->entry)[state->entrylen].desc = safe_strdup (tmp);
(state->entry)[state->entrylen].desc = safe_strdup (relpath);
(state->entry)[state->entrylen].notfolder = 1;
(state->entrylen)++;
......
......@@ -33,12 +33,9 @@
#include <unistd.h>
#include <ctype.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
......@@ -67,8 +64,6 @@ static int imap_check_acl (IMAP_DATA *idata);
static int imap_check_capabilities (IMAP_DATA *idata);
static int imap_create_mailbox (IMAP_DATA *idata, char *mailbox);
/* everything above should be moved into a separate imap header file */
void imap_make_sequence (char *buf, size_t buflen)
{
static int sequence = 0;
......@@ -1016,6 +1011,8 @@ static int imap_get_delim (IMAP_DATA *idata, CONNECTION *conn)
return 0;
}
/* imap_parse_path: given an IMAP mailbox name, return host, port
* and a path IMAP servers will recognise. */
int imap_parse_path (char *path, char *host, size_t hlen, int *port,
char **mbox)
{
......@@ -2232,6 +2229,9 @@ int imap_complete(char* dest, size_t dlen, char* path) {
if (list_word)
{
/* store unquoted */
imap_unquote_string (list_word);
/* if the folder isn't selectable, append delimiter to force browse
* to enter it on second tab. */
if (noselect)
......
......@@ -27,6 +27,8 @@ int imap_check_mailbox (CONTEXT *ctx, int *index_hint);
int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno);
int imap_open_mailbox (CONTEXT *ctx);
int imap_open_mailbox_append (CONTEXT *ctx);
int imap_parse_path (char *path, char *host, size_t hlen, int *port,
char **mbox);
int imap_select_mailbox (CONTEXT *ctx, const char* path);
int imap_sync_mailbox (CONTEXT *ctx, int expunge);
void imap_fastclose_mailbox (CONTEXT *ctx);
......
......@@ -23,11 +23,11 @@
#include "imap_socket.h"
/* -- symbols -- */
#define IMAP_PORT 143
/* number of entries in the hash table */
#define IMAP_CACHE_LEN 10
#define IMAP_PORT 143
#define SEQLEN 5
enum
......@@ -82,9 +82,9 @@ enum
ACL, /* RFC 2086: IMAP4 ACL extension */
NAMESPACE, /* RFC 2342: IMAP4 Namespace */
ACRAM_MD5, /* RFC 2195: CRAM-MD5 authentication */
/* From here down, we don't care */
AKERBEROS_V4, /* AUTH=KERBEROS_V4 */
AGSSAPI, /* AUTH=GSSAPI */
AGSSAPI, /* RFC 1731: GSSAPI authentication */
/* From here down, we don't care */
ALOGIN, /* AUTH=LOGIN */
AUTH_LOGIN, /* AUTH-LOGIN */
APLAIN, /* AUTH=PLAIN */
......@@ -173,8 +173,6 @@ char *imap_next_word (char *s);
int imap_open_connection (IMAP_DATA *idata, CONNECTION *conn);
int imap_parse_list_response(CONNECTION *conn, char *buf, int buflen,
char **name, int *noselect, int *noinferiors, char *delim);
int imap_parse_path (char *path, char *host, size_t hlen, int *port,
char **mbox);
void imap_quote_string (char *dest, size_t slen, const char *src);
void imap_unquote_string (char *s);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment