diff options
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | configure.in | 29 | ||||
-rw-r--r-- | env.c | 1 | ||||
-rw-r--r-- | fetchmail.c | 5 | ||||
-rw-r--r-- | fetchmail.h | 1 | ||||
-rw-r--r-- | fetchmail.man | 15 | ||||
-rw-r--r-- | imap.c | 183 | ||||
-rw-r--r-- | rcfile_l.l | 1 |
8 files changed, 232 insertions, 5 deletions
@@ -7,6 +7,7 @@ when user's pop3 host isn't the same as his SMTP host.) * Allow -c -F to work to flush messages without retrieval. * Read from /etc/fetchmailrc before ~/.fetchmailrc? +* Do is_host_alias checks by comparing IP addresses rather than names? Other TO-DO items: @@ -18,6 +19,7 @@ fetchmail-4.3.5 () * Added Kent Robotti's fetchsetup configuration script. * Corrected buggy handling of `expunge 0'. +* GSSAPI support from Brendan Cully <brendan@kublai.com>. fetchmail-4.3.4 (Fri Dec 5 12:39:31 EST 1997) * Yet another attempt on the Compuserve RPA moving target. diff --git a/configure.in b/configure.in index c9a10f1b..677ef6f3 100644 --- a/configure.in +++ b/configure.in @@ -196,6 +196,35 @@ then AC_DEFINE(OPIE,1) fi +### use option --with-gssapi=DIR to compile in GSSAPI support +AC_ARG_WITH(gssapi, + [ --with-gssapi[=DIR] compile in GSSAPI support using libraries in DIR]) +if test "$with-gssapi" = "yes" +then + GSSAPIDIR="/usr /usr/local /usr/athena" +else + GSSAPIDIR="$with_gssapi" +fi +if test "$GSSAPIDIR" != "" -a "$GSSAPIDIR" != "no" +then + AC_MSG_CHECKING([for gssapi]) + for curgssapidir in $GSSAPIDIR + do + if test -f $curgssapidir/include/gssapi/gssapi.h + then + CEFLAGS="$CEFLAGS -DGSSAPI -I$curgssapidir/include" + LDEFLAGS="$LDEFLAGS -L$curgssapidir/lib" + LIBS="$LIBS -lgssapi_krb5 -lkrb5" + AC_MSG_RESULT([in $curgssapidir]) + GSSAPIFOUND="yes" + fi + done + if test "$GSSAPIFOUND" != "yes" + then + AC_MSG_ERROR([not found]) + fi +fi + ### use option --with-kerberos=DIR to point at a Kerberos directory AC_ARG_WITH(kerberos, [ --with-kerberos=DIR point fetchmail compilation at a Kerberos directory]) @@ -97,6 +97,7 @@ char *showproto(int proto) case P_POP3: return("POP3"); break; case P_IMAP: return("IMAP"); break; case P_IMAP_K4: return("IMAP-K4"); break; + case P_IMAP_GSS: return("IMAP-GSS"); break; case P_APOP: return("APOP"); break; case P_RPOP: return("RPOP"); break; case P_ETRN: return("ETRN"); break; diff --git a/fetchmail.c b/fetchmail.c index b620467a..7f78eae6 100644 --- a/fetchmail.c +++ b/fetchmail.c @@ -295,7 +295,7 @@ int main (int argc, char **argv) for (ctl = querylist; ctl; ctl = ctl->next) if (ctl->active && !(implicitmode && ctl->server.skip)&&!ctl->password) { - if (ctl->server.preauthenticate == A_KERBEROS_V4 || ctl->server.protocol == P_IMAP_K4) + if (ctl->server.preauthenticate == A_KERBEROS_V4 || ctl->server.protocol == P_IMAP_K4 || ctl->server.protocol == P_IMAP_GSS) /* Server won't care what the password is, but there must be some non-null string here. */ ctl->password = ctl->remotename; @@ -311,7 +311,7 @@ int main (int argc, char **argv) ctl->password = xstrdup(p->password); } - if (ctl->server.protocol != P_ETRN && ctl->server.protocol != P_IMAP_K4 && !ctl->password) + if (ctl->server.protocol != P_ETRN && ctl->server.protocol != P_IMAP_K4 && ctl->server.protocol != P_IMAP_GSS && !ctl->password) { (void) sprintf(tmpbuf, "Enter password for %s@%s: ", ctl->remotename, ctl->server.pollname); @@ -874,6 +874,7 @@ static int query_host(struct query *ctl) break; case P_IMAP: case P_IMAP_K4: + case P_IMAP_GSS: #ifdef IMAP_ENABLE return(doIMAP(ctl)); #else diff --git a/fetchmail.h b/fetchmail.h index 5fafe8bf..55162750 100644 --- a/fetchmail.h +++ b/fetchmail.h @@ -11,6 +11,7 @@ #define P_APOP 6 #define P_RPOP 7 #define P_ETRN 8 +#define P_IMAP_GSS 9 #define KPOP_PORT 1109 diff --git a/fetchmail.man b/fetchmail.man index 566bc920..83cb6f91 100644 --- a/fetchmail.man +++ b/fetchmail.man @@ -166,6 +166,9 @@ IMAP2bis, IMAP4, or IMAP4rev1 (\fIfetchmail\fR autodetects their capabilities). .IP IMAP-K4 IMAP4, or IMAP4rev1 (\fIfetchmail\fR autodetects their capabilities) with RFC 1731 Kerberos v4 authentication. +.IP IMAP-GSS +IMAP4, or IMAP4rev1 (\fIfetchmail\fR autodetects their capabilities) +with RFC 1731 GSSAPI authentication. .IP ETRN Use the ESMTP ETRN option. .RE @@ -487,6 +490,13 @@ ticket from the mailserver at the start of each query. If you use IMAP-K4, \fIfetchmail\fR will expect the IMAP server to have RFC1731-conformant AUTHENTICATE KERBEROS_V4 capability, and will use it. .PP +If you use IMAP-GSS, \fIfetchmail\fR will expect the IMAP server to have +RFC1731-conformant AUTHENTICATE GSSAPI capability, and will use it. +Currently this has only been tested over Kerberos V, so you're expected +to already have a ticket-granting ticket. You may pass a username different +from your principal name using the standard \fB--user\fR command or by +the \fI.fetchmailrc\fR option \fBuser\fR. +.PP If you are using POP3, and the server issues a one-time-password challenge conforming to RFC1938, \fIfetchmail\fR will use your password as a pass phrase to generate the required response. This @@ -773,7 +783,7 @@ Specify DNS name of mailserver, overriding poll name T} proto[col] -p T{ Specify protocol (case insensitive): -POP2, POP3, IMAP, IMAP-K4, APOP, KPOP +POP2, POP3, IMAP, IMAP-K4, IMAP-GSS, APOP, KPOP T} port -P T{ Specify TCP/IP service port @@ -1071,6 +1081,7 @@ Legal protocol identifiers for use with the `protocol' keyword are: pop3 (or POP3) imap (or IMAP) imap-k4 (or IMAP-K4) + imap-gss (or IMAP-GSS) apop (or APOP) kpop (or KPOP) @@ -1457,7 +1468,7 @@ are technically legal but bizarre. Strange uses of quoting and embedded comments are likely to confuse it. .PP Use of any of the supported protocols other than POP3 with OTP or RPA, APOP, -KPOP, IMAP-K4, or ETRN requires that the program send unencrypted +KPOP, IMAP-K4, IMAP-GSS, or ETRN requires that the program send unencrypted passwords over the TCP/IP connection to the mailserver. This creates a risk that name/password pairs might be snaffled with a packet sniffer or more sophisticated monitoring software. Under Linux, the @@ -26,6 +26,11 @@ #include <krb.h> #endif /* KERBEROS_V4 */ +#ifdef GSSAPI +#include <gssapi/gssapi.h> +#include <gssapi/gssapi_generic.h> +#endif + #ifndef strstr /* glibc-2.1 declares this as a macro */ extern char *strstr(); /* needed on sysV68 R3V7.1. */ #endif /* strstr */ @@ -173,7 +178,7 @@ static int do_rfc1731(int sock, char *truename) } } - strncpy(srvrealm, krb_realmofhost(srvinst), (sizeof srvrealm)-1); + strncpy(srvrealm, (char *)krb_realmofhost(srvinst), (sizeof srvrealm)-1); srvrealm[(sizeof srvrealm)-1] = '\0'; if (p = strchr(srvinst, '.')) { *p = '\0'; @@ -328,6 +333,165 @@ static int do_rfc1731(int sock, char *truename) } #endif /* KERBEROS_V4 */ +#ifdef GSSAPI +#define GSSAUTH_P_NONE 1 +#define GSSAUTH_P_INTEGRITY 2 +#define GSSAUTH_P_PRIVACY 4 + +static int do_gssauth(int sock, char *hostname, char *username) +{ + 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[8192], buf2[8192], server_conf_flags; + unsigned long buf_size; + int result; + + /* first things first: get an imap ticket for host */ + sprintf(buf1, "imap@%s", hostname); + 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) { + error(0, -1, "Couldn't get service name for [%s]", buf1); + return PS_AUTHFAIL; + } + else if (outlevel == O_VERBOSE) { + maj_stat = gss_display_name(&min_stat, target_name, &request_buf, + &mech_name); + error(0, 0, "Using service name [%s]",request_buf.value); + maj_stat = gss_release_buffer(&min_stat, &request_buf); + } + + gen_send(sock, "AUTHENTICATE GSSAPI"); + + /* upon receipt of the GSSAPI authentication request, server returns + * null data ready response. */ + if (result = gen_recv(sock, buf1, sizeof buf1)) { + return result; + } + + /* now start the security context initialisation loop... */ + sec_token = GSS_C_NO_BUFFER; + context = GSS_C_NO_CONTEXT; + if (outlevel == O_VERBOSE) + error(0,0,"Sending credentials"); + do { + maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, + &context, target_name, NULL, 0, 0, NULL, sec_token, NULL, + &send_token, &cflags, NULL); + if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) { + error(0, -1,"Error exchanging credentials"); + gss_release_name(&min_stat, &target_name); + /* wake up server and await NO response */ + SockWrite(sock, "\r\n", 2); + if (result = gen_recv(sock, buf1, sizeof buf1)) + return result; + return PS_AUTHFAIL; + } + to64frombits(buf1, send_token.value, send_token.length); + gss_release_buffer(&min_stat, &send_token); + SockWrite(sock, buf1, strlen(buf1)); + SockWrite(sock, "\r\n", 2); + if (outlevel == O_VERBOSE) + error(0,0,"IMAP> %s", buf1); + if (maj_stat == GSS_S_CONTINUE_NEEDED) { + if (result = gen_recv(sock, buf1, sizeof buf1)) { + gss_release_name(&min_stat, &target_name); + return result; + } + request_buf.length = from64tobits(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 (result = gen_recv(sock, buf1, sizeof buf1)) { + return result; + } + request_buf.length = from64tobits(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) { + error(0,-1,"Couldn't unwrap security level data"); + gss_release_buffer(&min_stat, &send_token); + return PS_AUTHFAIL; + } + if (outlevel == O_VERBOSE) + error(0,0,"Credential exchange complete"); + /* first octet is security levels supported. We want none, for now */ + server_conf_flags = ((char *)send_token.value)[0]; + if ( !(((char *)send_token.value)[0] & GSSAUTH_P_NONE) ) { + error(0,-1,"Server requires integrity and/or privacy"); + gss_release_buffer(&min_stat, &send_token); + return PS_AUTHFAIL; + } + ((char *)send_token.value)[0] = 0; + buf_size = ntohl(*((long *)send_token.value)); + /* we don't care about buffer size if we don't wrap data */ + gss_release_buffer(&min_stat, &send_token); + if (outlevel == O_VERBOSE) { + error(0,0,"Unwrapped security level flags: %s%s%s", + server_conf_flags & GSSAUTH_P_NONE ? "N" : "-", + server_conf_flags & GSSAUTH_P_INTEGRITY ? "I" : "-", + server_conf_flags & GSSAUTH_P_PRIVACY ? "C" : "-"); + error(0,0,"Maximum GSS token size is %ld",buf_size); + } + + /* now respond in kind (hack!!!) */ + buf_size = htonl(buf_size); /* do as they do... only matters if we do enc */ + memcpy(buf1, &buf_size, 4); + buf1[0] = GSSAUTH_P_NONE; + strcpy(buf1+4, username); /* server decides if princ is user */ + request_buf.length = 4 + strlen(username) + 1; + request_buf.value = buf1; + maj_stat = gss_wrap(&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf, + &cflags, &send_token); + if (maj_stat != GSS_S_COMPLETE) { + error(0,-1,"Error creating security level request"); + return PS_AUTHFAIL; + } + to64frombits(buf1, send_token.value, send_token.length); + if (outlevel == O_VERBOSE) { + error(0,0,"Requesting authorisation as %s", username); + error(0,0,"IMAP> %s",buf1); + } + SockWrite(sock, buf1, strlen(buf1)); + SockWrite(sock, "\r\n", 2); + + /* we should be done. Get status and finish up */ + if (result = gen_recv(sock, buf1, sizeof buf1)) + return result; + if (strstr(buf1, "OK")) { + /* flush security context */ + if (outlevel == O_VERBOSE) + error(0, 0, "Releasing GSS credentials"); + maj_stat = gss_delete_sec_context(&min_stat, &context, &send_token); + if (maj_stat != GSS_S_COMPLETE) { + error(0, -1, "Error releasing credentials"); + return PS_AUTHFAIL; + } + /* 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 */ + return PS_SUCCESS; + } + + return PS_AUTHFAIL; +} +#endif /* GSSAPI */ + int imap_getauth(int sock, struct query *ctl, char *greeting) /* apply for connection authorization */ { @@ -367,6 +531,23 @@ int imap_getauth(int sock, struct query *ctl, char *greeting) return(ok); } +#ifdef GSSAPI + if (strstr(capabilities, "AUTH=GSSAPI")) + { + if (ctl->server.protocol == P_IMAP_GSS) + { + if (outlevel == O_VERBOSE) + error(0, 0, "GSS authentication is supported"); + return do_gssauth(sock, ctl->server.truename, ctl->remotename); + } + } + else if (ctl->server.protocol == P_IMAP_GSS) + { + error(0,-1, "Required GSS capability not supported by server"); + return(PS_AUTHFAIL); + } +#endif /* GSSAPI */ + #ifdef KERBEROS_V4 if (strstr(capabilities, "AUTH=KERBEROS_V4")) { @@ -90,6 +90,7 @@ options {/* EMPTY */} (pop2)|(POP2) { yylval.proto = P_POP2; return PROTO; } (pop3)|(POP3) { yylval.proto = P_POP3; return PROTO; } (imap-k4)|(IMAP-K4) { yylval.proto = P_IMAP_K4; return PROTO; } +(imap-gss)|(IMAP-GSS) { yylval.proto = P_IMAP_GSS; return PROTO; } (imap)|(IMAP) { yylval.proto = P_IMAP; return PROTO; } (apop)|(APOP) { yylval.proto = P_APOP; return PROTO; } (etrn)|(ETRN) { yylval.proto = P_ETRN; return PROTO; } |