Commit 4a53c139 authored by Thomas Roessler's avatar Thomas Roessler

Brendan Cully's patch from <20000212185021.A7365@xanadu.kublai.com>.

parent 522c968c
......@@ -9,6 +9,7 @@ configure
config.cache
config.log
config.status
keymap_alldefs.h
keymap_defs.h
makedoc
mutt_dotlock
......
......@@ -38,7 +38,9 @@ mutt_SOURCES = $(BUILT_SOURCES) \
muttbug_SOURCES = muttbug.sh.in
mutt_LDADD = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAP) $(GSSLIBS) $(INTLLIBS)
mutt_LDADD = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAP) $(MUTTLIBS) \
$(INTLLIBS)
mutt_DEPENDENCIES = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAPDEPS) $(INTLDEPS)
makedoc_SOURCES = makedoc.c
......@@ -47,7 +49,9 @@ CPP=@CPP@
DEFS=-DSHAREDIR=\"$(sharedir)\" -DSYSCONFDIR=\"$(sysconfdir)\" \
-DBINDIR=\"$(bindir)\" -DHAVE_CONFIG_H=1
INCLUDES=-I. $(IMAP_INCLUDES) -I$(includedir)
# top_srcdir is for building outside of the source tree
INCLUDES=-I$(top_srcdir) -I. $(IMAP_INCLUDES) -I$(includedir)
non_us_sources = pgp.c pgpinvoke.c pgpkey.c pgplib.c sha1dgst.c \
gnupgparse.c sha.h sha_locl.h \
......
......@@ -172,7 +172,7 @@ folder_format_str (char *dest, size_t destlen, char op, const char *src,
case 'f':
#ifdef USE_IMAP
if (mx_is_imap (folder->ff->name))
if (folder->ff->imap)
strfcpy (fn, NONULL(folder->ff->desc), sizeof (fn));
else
#endif
......@@ -207,10 +207,13 @@ folder_format_str (char *dest, size_t destlen, char op, const char *src,
else
{
#ifdef USE_IMAP
if (mx_is_imap (folder->ff->name))
if (folder->ff->imap)
{
sprintf (permission, "IMAP %c%c",
folder->ff->inferiors ? '+' : ' ',
folder->ff->selectable ? 'S' : ' ');
snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
snprintf (dest, destlen, tmp, "IMAP");
snprintf (dest, destlen, tmp, permission);
}
#endif
}
......@@ -328,9 +331,8 @@ static void add_folder (MUTTMENU *m, struct browser_state *state,
(state->entry)[state->entrylen].name = safe_strdup (name);
(state->entry)[state->entrylen].desc = safe_strdup (name);
#ifdef USE_IMAP
(state->entry)[state->entrylen].notfolder = 0;
(state->entry)[state->entrylen].imap = 0;
#endif
(state->entrylen)++;
}
......@@ -421,7 +423,7 @@ static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state)
do
{
#ifdef USE_IMAP
if (tmp->path[0] == '{')
if (mx_is_imap (tmp->path))
{
add_folder (menu, state, tmp->path, NULL, tmp->new);
continue;
......@@ -623,7 +625,7 @@ void _mutt_select_file (char *f, size_t flen, int buffy,
(S_ISLNK (state.entry[menu->current].mode) &&
link_is_dir (LastDir, state.entry[menu->current].name))
#ifdef USE_IMAP
|| state.entry[menu->current].notfolder
|| state.entry[menu->current].inferiors
#endif
)
{
......@@ -645,7 +647,7 @@ void _mutt_select_file (char *f, size_t flen, int buffy,
if ((mx_get_magic (buf) <= 0)
#ifdef USE_IMAP
|| state.entry[menu->current].notfolder
|| state.entry[menu->current].inferiors
#endif
)
{
......@@ -681,8 +683,17 @@ void _mutt_select_file (char *f, size_t flen, int buffy,
#ifdef USE_IMAP
else if (state.imap_browse)
{
int n;
strfcpy (LastDir, state.entry[menu->current].name,
sizeof (LastDir));
/* tack on delimiter here */
if ((state.entry[menu->current].delim != '\0') &&
(n = strlen (LastDir)+1) < sizeof (LastDir))
{
LastDir[n] = '\0';
LastDir[n-1] = state.entry[menu->current].delim;
}
}
#endif
else
......@@ -806,7 +817,7 @@ void _mutt_select_file (char *f, size_t flen, int buffy,
break;
case OP_DELETE_MAILBOX:
if (!mx_is_imap (state.entry[menu->current].name))
if (!state.entry[menu->current].imap)
mutt_error (_("Delete is only supported for IMAP mailboxes"));
else
{
......@@ -1058,6 +1069,16 @@ void _mutt_select_file (char *f, size_t flen, int buffy,
break;
}
#ifdef USE_IMAP
if (state.entry[menu->current].selectable)
{
strfcpy (f, state.entry[menu->current].name, flen);
destroy_state (&state);
mutt_menuDestroy (&menu);
return;
}
else
#endif
if (S_ISDIR (state.entry[menu->current].mode) ||
(S_ISLNK (state.entry[menu->current].mode) &&
link_is_dir (LastDir, state.entry[menu->current].name)))
......
......@@ -30,7 +30,11 @@ struct folder_file
char *name;
char *desc;
#ifdef USE_IMAP
short notfolder;
char delim;
unsigned imap : 1;
unsigned selectable : 1;
unsigned inferiors : 1;
#endif
unsigned tagged : 1;
unsigned is_new : 1;
......
......@@ -65,7 +65,7 @@ if test -f $srcdir/EXPORTABLE ; then
else
SUBVERSION="i"
AC_ARG_ENABLE(pgp, [ --disable-pgp Disable PGP support],
AC_ARG_ENABLE(pgp, [ --disable-pgp Disable PGP support],
[ if test x$enableval = xno ; then
HAVE_PGP=no
fi
......@@ -79,7 +79,7 @@ else
OPS="$OPS \$(srcdir)/OPS.PGP"
fi
AC_ARG_WITH(mixmaster, [ --with-mixmaster[=PATH] include Mixmaster support],
AC_ARG_WITH(mixmaster, [ --with-mixmaster[=PATH] Include Mixmaster support],
[if test -x "$withval" ; then
MIXMASTER="$withval"
else
......@@ -103,7 +103,7 @@ if test $ISPELL != no; then
AC_DEFINE_UNQUOTED(ISPELL, "$ISPELL")
fi
AC_ARG_WITH(slang, [ --with-slang[=DIR] use S-Lang instead of ncurses],
AC_ARG_WITH(slang, [ --with-slang[=DIR] Use S-Lang instead of ncurses],
[AC_CACHE_CHECK([if this is a BSD system], mutt_cv_bsdish,
[AC_TRY_RUN([#include <sys/param.h>
......@@ -151,7 +151,6 @@ main ()
fi
fi
AC_MSG_RESULT($mutt_cv_slang)
LIBS="$LIBS -lslang -lm"
if test $mutt_cv_bsdish = yes; then
AC_CHECK_LIB(termlib, main)
fi
......@@ -159,17 +158,16 @@ main ()
AC_DEFINE(HAVE_COLOR)
MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS resize.o"
dnl --- try to link a sample program to check if we're ok
dnl --- now that we've found it, check the link
AC_MSG_CHECKING(if I can compile a test SLang program)
AC_TRY_LINK([], [SLtt_get_terminfo ();],
[AC_MSG_RESULT(yes)],
[AC_MSG_ERROR(unable to compile. check config.log)])
AC_CHECK_LIB(slang, SLtt_get_terminfo,
[MUTTLIBS="$MUTTLIBS -lslang -lm"],
[AC_MSG_ERROR(unable to compile. check config.log)], -lm)
],
[mutt_cv_curses=/usr
AC_ARG_WITH(curses, [ --with-curses=DIR ncurses is installed in ],
AC_ARG_WITH(curses, [ --with-curses=DIR Where ncurses is installed ],
[if test $withval != yes; then
mutt_cv_curses=$withval
fi
......@@ -180,13 +178,13 @@ main ()
AC_CHECK_LIB(ncurses, initscr,
[LIBS="$LIBS -lncurses"
[MUTTLIBS="$MUTTLIBS -lncurses"
if test x$mutt_cv_curses = x/usr -a -d /usr/include/ncurses; then
CPPFLAGS="$CPPFLAGS -I/usr/include/ncurses"
fi
AC_CHECK_HEADERS(ncurses.h)],
[LIBS="$LIBS -lcurses"
[MUTTLIBS="$MUTTLIBS -lcurses"
if test -f /usr/ccs/lib/libcurses.a; then
LDFLAGS="$LDFLAGS -L/usr/ccs/lib"
else
......@@ -196,9 +194,13 @@ main ()
fi
fi])
old_LIBS="$LIBS"
LIBS="$LIBS $MUTTLIBS"
AC_CHECK_FUNC(start_color, [AC_DEFINE(HAVE_COLOR)])
AC_CHECK_FUNCS(typeahead bkgdset curs_set meta use_default_colors)
AC_CHECK_FUNCS(resizeterm, [MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS resize.o"])
AC_CHECK_FUNCS(resizeterm,
[MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS resize.o"])
LIBS="$old_LIBS"
])
AC_HEADER_STDC
......@@ -248,7 +250,7 @@ AC_CHECK_FUNCS(strftime, break, [AC_CHECK_LIB(intl, strftime)])
dnl AIX may not have fchdir()
AC_CHECK_FUNCS(fchdir, [AC_DEFINE(HAVE_FCHDIR)], [mutt_cv_fchdir=no])
AC_ARG_WITH(regex, [ --with-regex Use the GNU regex library ],
AC_ARG_WITH(regex, [ --with-regex Use the GNU regex library ],
[mutt_cv_regex=yes],
[AC_CHECK_FUNCS(regcomp, mutt_cv_regex=no, mutt_cv_regex=yes)])
......@@ -272,7 +274,7 @@ if test $mutt_cv_regex = yes; then
fi
AC_ARG_WITH(homespool, [ --with-homespool[=FILE] file in user's directory where new mail is spooled], with_homespool=${withval})
AC_ARG_WITH(homespool, [ --with-homespool[=FILE] File in user's directory where new mail is spooled], with_homespool=${withval})
if test x$with_homespool != x; then
if test $with_homespool = yes; then
with_homespool=mailbox
......@@ -282,7 +284,7 @@ if test x$with_homespool != x; then
AC_DEFINE(USE_DOTLOCK)
mutt_cv_setgid=no
else
AC_ARG_WITH(mailpath, [ --with-mailpath=DIR directory where spool mailboxes are located],
AC_ARG_WITH(mailpath, [ --with-mailpath=DIR Directory where spool mailboxes are located],
[mutt_cv_mailpath=$withval],
[ AC_CACHE_CHECK(where new mail is stored, mutt_cv_mailpath,
[mutt_cv_mailpath=no
......@@ -338,7 +340,7 @@ int main (int argc, char **argv)
fi
fi
AC_ARG_ENABLE(external_dotlock, [ --enable-external-dotlock Force use of an external dotlock program],
AC_ARG_ENABLE(external_dotlock, [ --enable-external-dotlock Force use of an external dotlock program],
[mutt_cv_external_dotlock="$enableval"])
if test "x$mutt_cv_setgid" = "xyes" || test "x$mutt_cv_fchdir" = "xno" \
......@@ -353,7 +355,7 @@ fi
AC_SUBST(DOTLOCK_TARGET)
AC_ARG_WITH(libdir, [ --with-libdir=PATH specify where to put arch dependent files],
AC_ARG_WITH(libdir, [ --with-libdir=PATH Specify where to put arch dependent files],
[mutt_cv_libdir=$withval],
[ AC_CACHE_CHECK(where to put architecture-dependent files,
mutt_cv_libdir,
......@@ -363,7 +365,7 @@ AC_ARG_WITH(libdir, [ --with-libdir=PATH specify where to put arch depend
libdir=$mutt_cv_libdir
AC_SUBST(libdir)
AC_ARG_WITH(sharedir, [ --with-sharedir=PATH specify where to put arch independent files],
AC_ARG_WITH(sharedir, [ --with-sharedir=PATH Specify where to put arch independent files],
[mutt_cv_sharedir=$withval],
[ AC_CACHE_CHECK(where to put architecture-independent data files,
mutt_cv_sharedir,
......@@ -378,7 +380,7 @@ sharedir=$mutt_cv_sharedir
AC_SUBST(sharedir)
mutt_cv_charmaps=/usr/share/i18n/charmaps
AC_ARG_WITH(charmaps, [--with-charmaps=PATH specify where on the system mutt can find character set definitions],
AC_ARG_WITH(charmaps, [ --with-charmaps=PATH Where to find character set definitions],
[mutt_cv_charmaps=$withval])
mutt_cv_fake_charmaps=yes
......@@ -406,7 +408,7 @@ charmaps=$mutt_cv_charmaps
AC_SUBST(charmaps)
AM_CONDITIONAL(BUILD_CHARMAPS, test x$need_charmaps = xyes)
AC_ARG_WITH(docdir, [ --with-docdir=PATH specify where to put the documentation],
AC_ARG_WITH(docdir, [ --with-docdir=PATH Specify where to put the documentation],
[mutt_cv_docdir=$withval],
[ AC_CACHE_CHECK(where to put the documentation,
mutt_cv_docdir,
......@@ -436,7 +438,7 @@ AC_ARG_WITH(domain, [ --with-domain=DOMAIN Specify your DNS domain name ]
AC_DEFINE_UNQUOTED(DOMAIN, "$withval")
fi])
AC_ARG_ENABLE(pop, [ --enable-pop Enable POP3 support],
AC_ARG_ENABLE(pop, [ --enable-pop Enable POP3 support],
[ if test x$enableval = xyes ; then
AC_DEFINE(USE_POP)
AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt))
......@@ -457,8 +459,13 @@ AC_ARG_ENABLE(imap, [ --enable-imap Enable IMAP support],
])
AM_CONDITIONAL(BUILD_IMAP, test x$need_imap = xyes)
AC_ARG_WITH(gss, [ --with-gss[=DIR] Compile in GSSAPI authentication for IMAP],
AC_ARG_WITH(gss, [ --with-gss[=DIR] Compile in GSSAPI authentication for IMAP],
[
if test "$need_imap" != "yes"
then
AC_MSG_ERROR([GSS support is only for IMAP, but IMAP is not enabled])
fi
if test "$with_gss" != "no"
then
if test "$with_gss" != "yes"
......@@ -473,24 +480,29 @@ AC_ARG_WITH(gss, [ --with-gss[=DIR] Compile in GSSAPI authentication for
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"
MUTTLIBS="$MUTTLIBS -lgssapi_krb5 -lkrb5"
AC_DEFINE(USE_GSS)
need_gss="yes"
fi
])
AM_CONDITIONAL(USE_GSS, test x$need_gss = xyes)
AC_ARG_ENABLE(ssl, [ --enable-ssl Enable SSL support],
AC_ARG_ENABLE(ssl, [ --enable-ssl Enable SSL support for IMAP],
[ if test "$need_imap" != "yes"; then
AC_MSG_ERROR([Sorry, SSL support only for IMAP])
AC_MSG_ERROR([SSL support is only for IMAP, but IMAP is not enabled])
fi
saved_LIBS="$LIBS"
AC_CHECK_LIB(crypto, X509_new,, AC_MSG_ERROR([Unable to find SSL library]))
AC_CHECK_LIB(ssl, SSL_new,, AC_MSG_ERROR([Unable to find SSL library]), -lcrypto)
AC_DEFINE(USE_SSL)
LIBS="$saved_LIBS"
MUTTLIBS="$MUTTLIBS -lssl -lcrypto"
need_ssl=yes
])
AM_CONDITIONAL(USE_SSL, test x$need_ssl = xyes)
AC_ARG_ENABLE(debug, [ --enable-debug Enable debugging support],
AC_ARG_ENABLE(debug, [ --enable-debug Enable debugging support],
[ if test x$enableval = xyes ; then
AC_DEFINE(DEBUG)
fi
......@@ -544,10 +556,10 @@ AC_ARG_ENABLE(exact-address, [ --enable-exact-address enable regeneration o
AC_DEFINE(EXACT_ADDRESS)
fi])
AC_SUBST(MUTTLIBS)
AC_SUBST(MUTT_LIB_OBJECTS)
AC_SUBST(LIBIMAP)
AC_SUBST(LIBIMAPDEPS)
AC_SUBST(GSSLIBS)
MUTT_AM_GNU_GETTEXT
CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/intl"
......
......@@ -1994,14 +1994,38 @@ variable, which defaults to every 60 seconds.
Mutt is designed to work with IMAP4rev1 servers, and was originally tested
with both the UWash IMAP server v11.241 and the Cyrus IMAP server v1.5.14.
Nowadays it is primarily developed against UW-IMAP 12.250. It works
more-or-less correctly against Cyrus 1.6.11, though there are a few minor
quirks in the folder browser.
Nowadays it is primarily developed against UW-IMAP 12.250. It appears
to work more-or-less correctly against Cyrus 1.6.11 as well.
Note that if you are using mbox as the mail store on UW servers prior to
v12.250, the server has been reported to disconnect a client if another client
selects the same folder.
<sect2>The Folder Browser
<p>
As of version 1.2, mutt supports browsing mailboxes on an IMAP
server. This is mostly the same as the local file browser, with the
following differences:
<itemize>
<item>In lieu of file permissions, mutt displays the string "IMAP",
possibly followed by the symbol "S", indicating that the entry is
selectable (contains messages), and/or the symbol "+", indicating
that the entry may contain subfolders. If your server is like the
UW-IMAP server, you won't see both symbols, but if you are using a
Cyrus-like server folders will often contain both messages and
subfolders.
<item>For the case where an entry can contain both messages and
subfolders, the selection key (bound to <tt>enter</tt> by default)
will choose to descend into the subfolder view. If you wish to view
the messages in that folder, you must use <tt>view-file</tt> instead
(bound to <tt>space</tt> by default).
<item>You can delete mailboxes with the <tt>delete-mailbox</tt>
command (bound to <tt>d</tt> by default. You may also
<tt>subscribe</tt> and <tt>unsubscribe</tt> to mailboxes (normally
these are bound to <tt>s</tt> and <tt>u</tt>, respectively).
</itemize>
<sect2>Authentication
<p>
......
......@@ -2,6 +2,10 @@
AUTOMAKE_OPTIONS = foreign
if USE_GSS
GSSSOURCES = auth_gss.c
endif
if USE_SSL
SSLSOURCES = imap_ssl.c
SSLHEADERS = imap_ssl.h
......@@ -15,4 +19,4 @@ noinst_LIBRARIES = libimap.a
noinst_HEADERS = imap_private.h imap_socket.h md5.h message.h $(SSLHEADERS)
libimap_a_SOURCES = auth.c browse.c command.c imap.c imap.h md5c.c message.c \
socket.c util.c $(SSLSOURCES)
socket.c util.c $(GSSSOURCES) $(SSLSOURCES)
/*
* Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
* Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
* Copyright (C) 1999 Brendan Cully <brendan@kublai.com>
* Copyright (C) 1999-2000 Brendan Cully <brendan@kublai.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -27,16 +27,9 @@
#define MD5_BLOCK_LEN 64
#define MD5_DIGEST_LEN 16
/* external authenticator prototypes */
#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
int imap_auth_gss (IMAP_DATA* idata, const char* user);
#endif
/* forward declarations */
......@@ -45,9 +38,6 @@ static void hmac_md5 (const char* password, char* challenge,
static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user,
const char* pass);
static int imap_auth_anon (IMAP_DATA *idata);
#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,
......@@ -100,229 +90,6 @@ 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), "imap@%s", idata->conn->mx.host);
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,
GSS_C_NO_OID,
GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
0,
GSS_C_NO_CHANNEL_BINDINGS,
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);
mutt_socket_write(idata->conn, "*\r\n");
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);
mutt_socket_write(idata->conn, "*\r\n");
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"));
mutt_socket_write(idata->conn, "*\r\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"));
mutt_socket_write(idata->conn, "*\r\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,
......
/*
* Copyright (C) 1999-2000 Brendan Cully <brendan@kublai.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* GSS login/authentication code */
#include "mutt.h"
#include "imap_private.h"
#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
/* imap_auth_gss: AUTH=GSSAPI support. Used unconditionally if the server
* supports it */
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), "imap@%s", idata->conn->mx.host);
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);