From b749096ca1c0842db21742e53d8f9766412d2c5d Mon Sep 17 00:00:00 2001
From: "Eric S. Raymond" <esr@thyrsus.com>
Date: Sun, 11 Feb 2001 21:32:24 +0000
Subject: Authentication completely refactored.

svn path=/trunk/; revision=3067
---
 Makefile.in |  16 ++--
 NEWS        |  10 ++-
 fetchmail.h |   2 +
 imap.c      | 255 ------------------------------------------------------------
 pop3.c      |  29 ++++++-
 5 files changed, 47 insertions(+), 265 deletions(-)

diff --git a/Makefile.in b/Makefile.in
index a862035e..1488f96f 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -97,8 +97,9 @@ CTAGS = ctags
 protobjs = rcfile_y.o rcfile_l.o socket.o getpass.o pop2.o pop3.o imap.o \
        etrn.o odmr.o fetchmail.o idle.o env.o options.o daemon.o driver.o \
        sink.o rfc822.o smtp.o xmalloc.o uid.o mxget.o md5ify.o cram.o \
-       kerberos.o rpa.o interface.o netrc.o base64.o error.o unmime.o conf.o \
-       checkalias.o smbdes.o smbencrypt.o smbmd4.o smbutil.o ipv6-connect.o
+       kerberos.o gssapi.o opie.o rpa.o interface.o netrc.o base64.o \
+       error.o unmime.o conf.o checkalias.o smbdes.o smbencrypt.o smbmd4.o \
+       smbutil.o ipv6-connect.o
 
 objs = $(protobjs) $(extras) $(EXTRAOBJ)
 
@@ -109,11 +110,12 @@ srcs = $(srcdir)/socket.c $(srcdir)/getpass.c $(srcdir)/pop2.c 		\
        $(srcdir)/driver.c $(srcdir)/sink.c $(srcdir)/rfc822.c 		\
        $(srcdir)/smtp.c $(srcdir)/xmalloc.c $(srcdir)/uid.c 		\
        $(srcdir)/mxget.c $(srcdir)/md5ify.c $(srcdir)/cram.c  		\
-       $(srcdir)/kerberos.c $(srcdir)/rpa.c $(srcdir)/interface.c	\
-       $(srcdir)/netrc.c $(srcdir)/base64.c $(srcdir)/error.c 		\
-       $(srcdir)/unmime.c $(srcdir)/conf.c $(srcdir)/checkalias.c 	\
-       $(srcdir)/smbdes.c $(srcdir)/smbencrypt.c $(srcdir)/smbmd4.c	\
-       $(srcdir)/smbutil.c $(srcdir)/ipv6-connect.c
+       $(srcdir)/kerberos.c $(srcdir)/gssapi.c  $(srcdir)/opie.c	\
+       $(srcdir)/rpa.c $(srcdir)/interface.c $(srcdir)/netrc.c		\
+       $(srcdir)/base64.c $(srcdir)/error.c $(srcdir)/unmime.c		\
+       $(srcdir)/conf.c $(srcdir)/checkalias.c $(srcdir)/smbdes.c	\
+       $(srcdir)/smbencrypt.c $(srcdir)/smbmd4.c $(srcdir)/smbutil.c	\
+       $(srcdir)/ipv6-connect.c
 
 .SUFFIXES:
 .SUFFIXES: .o .c .h .y .l .ps .dvi .info .texi
diff --git a/NEWS b/NEWS
index bb1ea9d4..b130d869 100644
--- a/NEWS
+++ b/NEWS
@@ -2,7 +2,15 @@
 
 (The `lines' figures total .c, .h, .l, and .y files under version control.)
 
-fetchmail-5.6.3 (Sun Feb 11 00:43:14 EST 2001), 20085 lines:
+* CRAM-MD5 authentication of IMAP and POP3 is working.  Tested against 
+  IMAP4rev1 2000.287 and v2000.70 POP3 gateway at neo.netnea.com.
+* Full support for POP3 AUTH (RFC1734) with KERBEROS_IV, GSSAPI, OTP.
+* Noted that Debian bugs #78963, #63064, #81312, #78796, #78363, #78149,
+  #68627, #67559, #63308, #63088, #71428 are fixed.
+* Resolved Debian bug #65505: fetchmail now returns a nonzero exit status
+  when interrupted before a successful fetch.
+
+fetchmail-5.6.4 (Sun Feb 11 00:43:14 EST 2001), 20085 lines:
 
 * ODMR port fix for AIX.
 * Dave Zarzycki's fix for former FAQ item F5 (%h and %p not being expanded).
diff --git a/fetchmail.h b/fetchmail.h
index 51d5bfeb..3f931bf2 100644
--- a/fetchmail.h
+++ b/fetchmail.h
@@ -521,6 +521,8 @@ int doETRN (struct query *);
 /* authentication functions */
 int do_cram_md5(int sock, char *command, struct query *ctl);
 int do_rfc1731(int sock, char *command, char *truename);
+int do_gssauth(int sock, char *command, char *hostname, char *username);
+int do_otp(int sock, char *command, struct query *ctl);
 
 /* miscellanea */
 struct query *hostalloc(struct query *); 
diff --git a/imap.c b/imap.c
index f74c3977..199c3c53 100644
--- a/imap.c
+++ b/imap.c
@@ -15,25 +15,9 @@
 #include  "fetchmail.h"
 #include  "socket.h"
 
-#ifdef GSSAPI
-#ifdef HAVE_GSSAPI_H
-#include <gssapi.h>
-#endif
-#ifdef HAVE_GSSAPI_GSSAPI_H
-#include <gssapi/gssapi.h>
-#endif
-#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
-#include <gssapi/gssapi_generic.h>
-#endif
-#ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
-#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
-#endif
-#endif
-
 #include  "i18n.h"
 
 #if OPIE_ENABLE
-#include <opie.h>
 #endif /* OPIE_ENABLE */
 
 #ifndef strstr		/* glibc-2.1 declares this as a macro */
@@ -133,245 +117,6 @@ int imap_ok(int sock, char *argbuf)
     }
 }
 
-#if OPIE_ENABLE
-static int do_otp(int sock, struct query *ctl)
-{
-    int i, rval;
-    char buffer[128];
-    char challenge[OPIE_CHALLENGE_MAX+1];
-    char response[OPIE_RESPONSE_MAX+1];
-
-    gen_send(sock, "AUTHENTICATE X-OTP");
-
-    if (rval = gen_recv(sock, buffer, sizeof(buffer)))
-	return rval;
-
-    if ((i = from64tobits(challenge, buffer)) < 0) {
-	report(stderr, _("Could not decode initial BASE64 challenge\n"));
-	return PS_AUTHFAIL;
-    };
-
-
-    to64frombits(buffer, ctl->remotename, strlen(ctl->remotename));
-
-    if (outlevel >= O_MONITOR)
-	report(stdout, "IMAP> %s\n", buffer);
-
-    /* best not to count on the challenge code handling multiple writes */
-    strcat(buffer, "\r\n");
-    SockWrite(sock, buffer, strlen(buffer));
-
-    if (rval = gen_recv(sock, buffer, sizeof(buffer)))
-	return rval;
-
-    if ((i = from64tobits(challenge, buffer)) < 0) {
-	report(stderr, _("Could not decode OTP challenge\n"));
-	return PS_AUTHFAIL;
-    };
-
-    rval = opiegenerator(challenge, !strcmp(ctl->password, "opie") ? "" : ctl->password, response);
-    if ((rval == -2) && !run.poll_interval) {
-	char secret[OPIE_SECRET_MAX+1];
-	fprintf(stderr, _("Secret pass phrase: "));
-	if (opiereadpass(secret, sizeof(secret), 0))
-	    rval = opiegenerator(challenge, secret, response);
-	memset(secret, 0, sizeof(secret));
-    };
-
-    if (rval)
-	return(PS_AUTHFAIL);
-
-    to64frombits(buffer, response, strlen(response));
-
-    if (outlevel >= O_MONITOR)
-	report(stdout, "IMAP> %s\n", buffer);
-    strcat(buffer, "\r\n");
-    SockWrite(sock, buffer, strlen(buffer));
-
-    if (rval = gen_recv(sock, buffer, sizeof(buffer)))
-	return rval;
-
-    if (strstr(buffer, "OK"))
-	return PS_SUCCESS;
-    else
-	return PS_AUTHFAIL;
-};
-#endif /* OPIE_ENABLE */
-
-#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_C_NT_HOSTBASED_SERVICE,
-        &target_name);
-    if (maj_stat != GSS_S_COMPLETE) {
-        report(stderr, _("Couldn't get service name for [%s]\n"), buf1);
-        return PS_AUTHFAIL;
-    }
-    else if (outlevel >= O_DEBUG) {
-        maj_stat = gss_display_name(&min_stat, target_name, &request_buf,
-            &mech_name);
-        report(stderr, _("Using service name [%s]\n"),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)
-        report(stdout, _("Sending credentials\n"));
-    do {
-        send_token.length = 0;
-	send_token.value = NULL;
-        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, 
-					NULL, 
-					NULL);
-        if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
-            report(stderr, _("Error exchanging credentials\n"));
-            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);
-	strcat(buf1, "\r\n");
-        SockWrite(sock, buf1, strlen(buf1));
-        if (outlevel >= O_MONITOR)
-            report(stdout, "IMAP> %s\n", 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) {
-        report(stderr, _("Couldn't unwrap security level data\n"));
-        gss_release_buffer(&min_stat, &send_token);
-        return PS_AUTHFAIL;
-    }
-    if (outlevel >= O_DEBUG)
-        report(stdout, _("Credential exchange complete\n"));
-    /* 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) ) {
-        report(stderr, _("Server requires integrity and/or privacy\n"));
-        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_DEBUG) {
-        report(stdout, _("Unwrapped security level flags: %s%s%s\n"),
-            server_conf_flags & GSSAUTH_P_NONE ? "N" : "-",
-            server_conf_flags & GSSAUTH_P_INTEGRITY ? "I" : "-",
-            server_conf_flags & GSSAUTH_P_PRIVACY ? "C" : "-");
-        report(stdout, _("Maximum GSS token size is %ld\n"),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) {
-        report(stderr, _("Error creating security level request\n"));
-        return PS_AUTHFAIL;
-    }
-    to64frombits(buf1, send_token.value, send_token.length);
-    if (outlevel >= O_DEBUG) {
-        report(stdout, _("Requesting authorization as %s\n"), username);
-        report(stdout, "IMAP> %s\n",buf1);
-    }
-    strcat(buf1, "\r\n");
-    SockWrite(sock, buf1, strlen(buf1));
-
-    /* we should be done. Get status and finish up */
-    do {
-	if (result = gen_recv(sock, buf1, sizeof buf1))
-           return result;
-    } while(strncmp(buf1, tag, strlen(tag)) != 0);
-    if (strstr(buf1, "OK")) {
-        /* flush security context */
-        if (outlevel >= O_DEBUG)
-            report(stdout, _("Releasing GSS credentials\n"));
-        maj_stat = gss_delete_sec_context(&min_stat, &context, &send_token);
-        if (maj_stat != GSS_S_COMPLETE) {
-            report(stderr, _("Error releasing credentials\n"));
-            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 */
-        gss_release_buffer(&min_stat, &send_token);
-        return PS_SUCCESS;
-    }
-
-    return PS_AUTHFAIL;
-}	
-#endif /* GSSAPI */
-
 #if NTLM_ENABLE
 #include "ntlm.h"
 
diff --git a/pop3.c b/pop3.c
index 7f0c9798..43aeace3 100644
--- a/pop3.c
+++ b/pop3.c
@@ -204,11 +204,20 @@ int pop3_getauth(int sock, struct query *ctl, char *greeting)
 	 * don't implement this, so don't do it at all unless the
 	 * server advertises APOP with <> in the greeting line.  This
 	 * certainly catches IMAP-2000's POP3 gateway.
+	 * 
+	 * These authentication methods are blessed by RFC1734,
+	 * POP3 AUTHentication command.
 	 */
 	if (strchr(greeting, '<') && gen_transact(sock, "AUTH") == 0)
 	{
 	    char buffer[10];
 	    flag has_cram = FALSE;
+#if defined(KERBEROS_IV)
+	    flag has_kerberos = FALSE;
+#endif /* defined(KERBEROS_IV) */
+#ifdef OPIE_ENABLE
+	    flag has_opie = FALSE;
+#endif /* OPIE_ENABLE */
 
 	    while ((ok = gen_recv(sock, buffer, sizeof(buffer))) == 0)
 	    {
@@ -216,9 +225,25 @@ int pop3_getauth(int sock, struct query *ctl, char *greeting)
 		    break;
 		if (strncasecmp(buffer, "CRAM-MD5", 8) == 0)
 		    has_cram = TRUE;
+#if defined(KERBEROS_IV)
+		if (strncasecmp(buffer, "KERBEROS_V4", 8) == 0)
+		    has_kerberos = TRUE;
+#endif /* defined(KERBEROS_IV) */
+#ifdef OPIE_ENABLE
+		if (strncasecmp(buffer, "SKEY", 8) == 0)
+		    has_opie = TRUE;
+#endif /* OPIE_ENABLE */
 	    }
-	    if (has_cram && !do_cram_md5(sock, "AUTH", ctl))
-		return(PS_SUCCESS);
+#if defined(KERBEROS_IV)
+	    if (has_kerberos)
+		return(do_rfc1731(sock, "AUTH", ctl->server.truename));
+#endif /* defined(KERBEROS_IV) */
+#ifdef OPIE_ENABLE
+	    if (has_opie)
+		do_otp(sock, ctl)
+#endif /* OPIE_ENABLE */
+	    if (has_cram)
+		return(do_cram_md5(sock, "AUTH", ctl));
 	}
 
 	/* ordinary validation, no one-time password or RPA */ 
-- 
cgit v1.2.3