diff options
author | Thomas Roessler <roessler@does-not-exist.org> | 1999-08-23 14:21:33 +0000 |
---|---|---|
committer | Thomas Roessler <roessler@does-not-exist.org> | 1999-08-23 14:21:33 +0000 |
commit | 0ad2b164b74ea0ac76e81a17ee5ac7f5f5c4b50e (patch) | |
tree | b132ad976295398a83614f67f40b7f4f96aa9a40 /imap | |
parent | 274bd0216e1c7420f34ad276e55239e80053c073 (diff) |
Inclue GSS authentization for IMAP.
Diffstat (limited to 'imap')
-rw-r--r-- | imap/auth.c | 251 | ||||
-rw-r--r-- | imap/browse.c | 6 | ||||
-rw-r--r-- | imap/imap.c | 10 | ||||
-rw-r--r-- | imap/imap.h | 2 | ||||
-rw-r--r-- | imap/imap_private.h | 10 |
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); |