summaryrefslogtreecommitdiffstats
path: root/imap
diff options
context:
space:
mode:
authorThomas Roessler <roessler@does-not-exist.org>1999-08-23 14:21:33 +0000
committerThomas Roessler <roessler@does-not-exist.org>1999-08-23 14:21:33 +0000
commit0ad2b164b74ea0ac76e81a17ee5ac7f5f5c4b50e (patch)
treeb132ad976295398a83614f67f40b7f4f96aa9a40 /imap
parent274bd0216e1c7420f34ad276e55239e80053c073 (diff)
Inclue GSS authentization for IMAP.
Diffstat (limited to 'imap')
-rw-r--r--imap/auth.c251
-rw-r--r--imap/browse.c6
-rw-r--r--imap/imap.c10
-rw-r--r--imap/imap.h2
-rw-r--r--imap/imap_private.h10
5 files changed, 258 insertions, 21 deletions
diff --git a/imap/auth.c b/imap/auth.c
index e04c6212..04a2b36f 100644
--- a/imap/auth.c
+++ b/imap/auth.c
@@ -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), "imap@%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)
{
diff --git a/imap/browse.c b/imap/browse.c
index b6213f17..adca6538 100644
--- a/imap/browse.c
+++ b/imap/browse.c
@@ -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)++;
diff --git a/imap/imap.c b/imap/imap.c
index 4d0fde52..2125e079 100644
--- a/imap/imap.c
+++ b/imap/imap.c
@@ -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)
diff --git a/imap/imap.h b/imap/imap.h
index 76eb59e1..88410492 100644
--- a/imap/imap.h
+++ b/imap/imap.h
@@ -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);
diff --git a/imap/imap_private.h b/imap/imap_private.h
index c338bac3..54e49994 100644
--- a/imap/imap_private.h
+++ b/imap/imap_private.h
@@ -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);