]> Pileus Git - ~andy/fetchmail/commitdiff
Make APOP an authenticator, rather than a protocol.
authorMatthias Andree <matthias.andree@gmx.de>
Wed, 22 Jun 2011 18:07:07 +0000 (20:07 +0200)
committerMatthias Andree <matthias.andree@gmx.de>
Wed, 22 Jun 2011 18:12:37 +0000 (20:12 +0200)
Incidentally also offers cram-md5 as authenticator in fetchmailconf.

NEWS
conf.c
driver.c
env.c
fetchmail.c
fetchmail.h
fetchmail.man
fetchmailconf.py
options.c
pop3.c
rcfile_l.l

diff --git a/NEWS b/NEWS
index f7ac4e458dfc53fcb58996c586b47784e5bf3ea1..7724c838c008e1e6598684a64f580ccf4654b075 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -68,6 +68,7 @@ NOTE THIS IS AN ALPHA RELEASE THAT HAS NOT BEEN THOROUGHLY TESTED!
   option will also mark the message with errors as seen.
   The default policy is to abort the session whenever a server error occurs.
   Contributed by Craig Brown.
+* Fetchmailconf offers cram-md5 and apop authentication.
 
 # REMOVED FEATURES
 * IMAP2 protocol support was removed.
@@ -109,6 +110,10 @@ NOTE THIS IS AN ALPHA RELEASE THAT HAS NOT BEEN THOROUGHLY TESTED!
 # CHANGES
 * A foreground fetchmail can now accept a few more options while another copy is
   running in the background.
+* APOP is no longer a protocol, but an authentication method. In order to use
+  it, use protocol POP3 auth APOP, or on the commandline, -p pop3 --auth apop.
+  If no authentication method is specified, APOP is automatically tried if
+  offered by the server before we resort to sending the password as clear text.
 
 --------------------------------------------------------------------------------
 
diff --git a/conf.c b/conf.c
index f02dbc76897d60633cba8ff6835ac92f6bae34aa..167aac118039b8a11cfd0f7c8c66c99516d4a945 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -258,26 +258,27 @@ void dump_config(struct runctl *runp, struct query *querylist)
            numdump("envskip", ctl->server.envskip);
            stringdump("qvirtual", ctl->server.qvirtual);
  
-           if (ctl->server.authenticate == A_ANY)
-               stringdump("auth", "any");
-           else if (ctl->server.authenticate == A_PASSWORD)
-               stringdump("auth", "password");
-           else if (ctl->server.authenticate == A_OTP)
-               stringdump("auth", "otp");
-           else if (ctl->server.authenticate == A_NTLM)
-               stringdump("auth", "ntlm");
-           else if (ctl->server.authenticate == A_CRAM_MD5)
-               stringdump("auth", "cram-md5");
-           else if (ctl->server.authenticate == A_GSSAPI)
-               stringdump("auth", "gssapi");
-           else if (ctl->server.authenticate == A_KERBEROS_V5)
-               stringdump("auth", "kerberos_v5");
-           else if (ctl->server.authenticate == A_SSH)
-               stringdump("auth", "ssh");
-           else if (ctl->server.authenticate == A_OTP)
-               stringdump("auth", "otp");
-           else if (ctl->server.authenticate == A_MSN)
-               stringdump("auth", "msn");
+           switch (ctl->server.authenticate) {
+               case A_ANY:
+                   stringdump("auth", "any"); break;
+               case A_PASSWORD:
+                   stringdump("auth", "password"); break;
+               case A_OTP:
+                   stringdump("auth", "otp"); break;
+               case A_NTLM:
+                   stringdump("auth", "ntlm"); break;
+               case A_CRAM_MD5:
+                   stringdump("auth", "cram-md5"); break;
+               case A_GSSAPI:
+                   stringdump("auth", "gssapi"); break;
+               case A_KERBEROS_V5:
+                   stringdump("auth", "kerberos_v5"); break;
+               case A_SSH:
+                   stringdump("auth", "ssh"); break;
+               case A_MSN:
+                   stringdump("auth", "msn"); break;
+               default: abort();
+           }
 
 #ifdef HAVE_RES_SEARCH
            booldump("dns", ctl->server.dns);
index 08bb9775def4280df9dd7ea7528e4dbcdacabc78..cbfe0e58e722c70c2c12a1c035ba2f8bdf82cd24 100644 (file)
--- a/driver.c
+++ b/driver.c
@@ -368,7 +368,7 @@ static int fetch_messages(int mailserver_socket, struct query *ctl,
         * could be "auto". */
        switch (ctl->server.protocol)
        {
-           case P_POP3: case P_APOP:
+           case P_POP3:
            fetchsizelimit = 1;
        }
 
diff --git a/env.c b/env.c
index cf535e34eb8b9944405c8f09fc8836013b66db66..7d03ae170a463502476d59b13bfae242fbd49a54 100644 (file)
--- a/env.c
+++ b/env.c
@@ -243,7 +243,6 @@ const char *showproto(int proto)
     case P_AUTO: return("auto");
 #ifdef POP3_ENABLE
     case P_POP3: return("POP3");
-    case P_APOP: return("APOP");
 #endif /* POP3_ENABLE */
 #ifdef IMAP_ENABLE
     case P_IMAP: return("IMAP");
index e4afe79447992e8f89ce5c16b5a8b933b49aab6b..5099314aea92202de0326d4620432e8cff7b4b47 100644 (file)
@@ -1815,7 +1815,6 @@ static int query_host(struct query *ctl)
        ctl->server.protocol = P_AUTO;
        break;
     case P_POP3:
-    case P_APOP:
 #ifdef POP3_ENABLE
        do {
            st = doPOP3(ctl);
@@ -1939,12 +1938,8 @@ static void dump_params (struct runctl *runp,
                printf(GT_("  Password will be prompted for.\n"));
            else if (outlevel >= O_VERBOSE)
            {
-               if (ctl->server.protocol == P_APOP)
-                   printf(GT_("  APOP secret = \"%s\".\n"),
-                          visbuf(ctl->password));
-               else
-                   printf(GT_("  Password = \"%s\".\n"),
-                                                       visbuf(ctl->password));
+               printf(GT_("  Password = \"%s\".\n"),
+                                   visbuf(ctl->password));
            }
        }
 
@@ -1990,6 +1985,11 @@ static void dump_params (struct runctl *runp,
        case A_SSH:
            printf(GT_("  End-to-end encryption assumed.\n"));
            break;
+       case A_APOP:
+           printf(GT_("  APOP authentication will be forced.\n"));
+           break;
+       default:
+           abort();
        }
        if (ctl->server.principal != (char *) NULL)
            printf(GT_("  Mail service principal is: %s\n"), ctl->server.principal);
index 4b7a00a7e20629254b78788534b394cf9edf5e80..272100f235235754567ac02f3276bcd809e37deb 100644 (file)
@@ -34,7 +34,6 @@ struct addrinfo;
 /* constants designating the various supported protocols */
 #define                P_AUTO          1
 #define                P_POP3          3
-#define                P_APOP          4
 #define                P_IMAP          6
 #define                P_ETRN          7
 #define                P_ODMR          8
@@ -61,6 +60,7 @@ struct addrinfo;
 #define                A_NTLM          2       /* Microsoft NTLM protocol */
 #define                A_CRAM_MD5      3       /* CRAM-MD5 shrouding (RFC2195) */
 #define                A_OTP           4       /* One-time password (RFC1508) */
+#define                A_APOP          5       /* POP3 APOP */
 #define                A_KERBEROS_V5   6       /* authenticate w/ Kerberos V5 */
 #define        A_GSSAPI        7       /* authenticate with GSSAPI */
 #define                A_SSH           8       /* authentication at session level */
index 38f86e4d4c5020f4dc470966ac3882c21d865018..8af8e432037bf045e34b66167b6c41607f149a4b 100644 (file)
@@ -267,9 +267,6 @@ Tries IMAP and POP3 (skipping any of these for which support
 has not been compiled in).
 .IP POP3
 Post Office Protocol 3
-.IP APOP
-Use POP3 with old-fashioned MD5-challenge authentication.
-Considered not resistant to man-in-the-middle attacks.
 .IP KPOP
 Use POP3 with Kerberos V5 authentication on port 1109.
 .IP SDPS
@@ -1103,7 +1100,8 @@ time to the server, which can verify it by checking its authorization
 database.
 
 \fBNote that APOP is no longer considered resistant against
-man-in-the-middle attacks.\fP
+man-in-the-middle attacks, and should not be used without a verified
+SSL/TLS connection.\fP
 .SS RETR or TOP
 \fBfetchmail\fP makes some efforts to make the server believe messages
 had not been retrieved, by using the TOP command with a large number of
@@ -1697,7 +1695,7 @@ Specify DNS name of mailserver, overriding poll name
 T}
 proto[col]     \-p     \&      T{
 Specify protocol (case insensitive):
-POP3, IMAP, APOP, KPOP
+POP3, IMAP, KPOP
 T}
 local[domains] \&      m       T{
 Specify domain(s) to be regarded as local
@@ -2165,20 +2163,20 @@ Legal protocol identifiers for use with the 'protocol' keyword are:
 .sp
 .nf
     auto (or AUTO) (legacy, to be removed from future release)
+
     pop3 (or POP3)
-    sdps (or SDPS)
+      sdps (or SDPS) (a POP3 variant specific to Demon)
+      kpop (or KPOP) (a Kerberos-based variant)
+
     imap (or IMAP)
-    apop (or APOP)
-    kpop (or KPOP)
 .fi
 .sp
 .PP
-Legal authentication types are 'any', 'password',
-\&'kerberos_v5' and 'gssapi', 'cram\-md5', 'otp', 'msn'
-(only for POP3), 'ntlm', 'ssh', 'external' (only IMAP).
+Legal authentication types are 'any', 'password', 'apop' (only for
+POP3), \&'kerberos_v5' and 'gssapi', 'cram\-md5', 'otp', 'msn'
+(only for POP3), 'ntlm', 'ssh', 'external' (only for IMAP).
 The 'password' type specifies
-authentication by normal transmission of a password (the password may be
-plain text or subject to protocol-specific encryption as in CRAM-MD5);
+authentication by normal transmission of a password;
 \&'kerberos_v5' tells \fBfetchmail\fP to try to get a Kerberos ticket at the
 start of each query instead, and send an arbitrary string as the
 password; and 'gssapi' tells fetchmail to use GSSAPI authentication.
index fb592a64d00ab72c7d1785d64da21e41518d28b4..b4b0a1ae7facd4431a39b776e39cefb071b9ab5e 100755 (executable)
@@ -446,14 +446,13 @@ ianaservices = {"pop3":110,
 # fetchmail protocol to IANA service name
 defaultports = {"auto":None,
                "POP3":"pop3",
-               "APOP":"pop3",
                "KPOP":"1109",
                "IMAP":"imap",
                "ETRN":"smtp",
                "ODMR":"odmr"}
 
 authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp",
-           "msn", "ntlm")
+           "msn", "ntlm", "apop", "cram-md5")
 
 listboxhelp = {
     'title' : 'List Selection Help',
@@ -1162,7 +1161,7 @@ class ServerEdit(Frame, MyWidget):
        # Compute the available protocols from the compile-time options
        protolist = ['auto']
        if 'pop3' in feature_options:
-           protolist = protolist + ["POP3", "APOP", "KPOP"]
+           protolist = protolist + ["POP3", "KPOP"]
        if 'sdps' in feature_options:
            protolist.append("SDPS")
        if 'imap' in feature_options:
index 59674e6d5570796d7878eeb5f2a2d06b5be75bae..5f1739c4f6e4cdbb50c2f1975f6367ca5ff0913b 100644 (file)
--- a/options.c
+++ b/options.c
@@ -330,8 +330,6 @@ int parsecmdline (int argc /** argument count */,
 #endif /* SDPS_ENABLE */
            else if (strcasecmp(optarg,"pop3") == 0)
                ctl->server.protocol = P_POP3;
-           else if (strcasecmp(optarg,"apop") == 0)
-               ctl->server.protocol = P_APOP;
            else if (strcasecmp(optarg,"kpop") == 0)
            {
                ctl->server.protocol = P_POP3;
@@ -389,6 +387,8 @@ int parsecmdline (int argc /** argument count */,
                ctl->server.authenticate = A_ANY;
            else if (strcmp(optarg, "msn") == 0)
                ctl->server.authenticate = A_MSN;
+           else if (strcmp(optarg, "apop") == 0)
+               ctl->server.authenticate = A_APOP;
            else {
                fprintf(stderr,GT_("Invalid authentication `%s' specified.\n"), optarg);
                errflag++;
diff --git a/pop3.c b/pop3.c
index aee0ec69ed2e15f3fe713bcd02725059b591be49..a84bbcc3aadb8740af1754eea454ea14abd6a394 100644 (file)
--- a/pop3.c
+++ b/pop3.c
@@ -262,12 +262,58 @@ static void set_peek_capable(struct query *ctl)
     peek_capable = !ctl->fetchall;
 }
 
+static int do_apop(int sock, struct query *ctl, char *greeting)
+{
+    char *start, *end;
+
+    /* build MD5 digest from greeting timestamp + password */
+    /* find start of timestamp */
+    start = strchr(greeting, '<');
+    if (!start) {
+       report(stderr,
+               GT_("Required APOP timestamp not found in greeting\n"));
+       return PS_AUTHFAIL;
+    }
+
+    /* find end of timestamp */
+    end = strchr(start + 1, '>');
+
+    if (!end || end == start + 1) {
+       report(stderr,
+               GT_("Timestamp syntax error in greeting\n"));
+       return(PS_AUTHFAIL);
+    } else {
+       *++end = '\0';
+    }
+
+    /* SECURITY: 2007-03-17
+     * Strictly validating the presented challenge for RFC-822
+     * conformity (it must be a msg-id in terms of that standard) is
+     * supposed to make attacks against the MD5 implementation
+     * harder[1]
+     *
+     * [1] "Security vulnerability in APOP authentication",
+     *     Gaëtan Leurent, fetchmail-devel, 2007-03-17 */
+    if (!rfc822_valid_msgid((unsigned char *)start)) {
+       report(stderr,
+               GT_("Invalid APOP timestamp.\n"));
+       return PS_AUTHFAIL;
+    }
+
+    /* copy timestamp and password into digestion buffer */
+    char *msg = (char *)xmalloc((end-start+1) + strlen(ctl->password) + 1);
+    strcpy(msg,start);
+    strcat(msg,ctl->password);
+    strcpy((char *)ctl->digest, MD5Digest((unsigned char *)msg));
+    free(msg);
+
+    return gen_transact(sock, "APOP %s %s", ctl->remotename, (char *)ctl->digest);
+}
+
 static int pop3_getauth(int sock, struct query *ctl, char *greeting)
 /* apply for connection authorization */
 {
     int ok;
-    char *start,*end;
-    char *msg;
 #ifdef OPIE_ENABLE
     char *challenge;
 #endif /* OPIE_ENABLE */
@@ -335,8 +381,12 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
         ctl->server.sdps = TRUE;
 #endif /* SDPS_ENABLE */
 
+    /* this is a leftover from the times 6.3.X and older when APOP was a
+     * "protocol" (P_APOP) rather than an authenticator (A_APOP),
+     * however, the switch is still useful because we can break; after
+     * an authenticator failed. */
    switch (ctl->server.protocol) {
-    case P_POP3:
+   case P_POP3:
 #ifdef RPA_ENABLE
        /* XXX FIXME: AUTH probing (RFC1734) should become global */
        /* CompuServe POP3 Servers as of 990730 want AUTH first for RPA */
@@ -517,31 +567,39 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
 #endif /* OPIE_ENABLE */
 
 #ifdef NTLM_ENABLE
-    /* MSN servers require the use of NTLM (MSN) authentication */
-    if (!strcasecmp(ctl->server.pollname, "pop3.email.msn.com") ||
-           ctl->server.authenticate == A_MSN)
-       return (do_pop3_ntlm(sock, ctl, 1) == 0) ? PS_SUCCESS : PS_AUTHFAIL;
-    if (ctl->server.authenticate == A_NTLM || (has_ntlm && ctl->server.authenticate == A_ANY)) {
-       ok = do_pop3_ntlm(sock, ctl, 0);
-        if (ok == 0 || ctl->server.authenticate != A_ANY)
-           break;
-    }
+       /* MSN servers require the use of NTLM (MSN) authentication */
+       if (!strcasecmp(ctl->server.pollname, "pop3.email.msn.com") ||
+               ctl->server.authenticate == A_MSN)
+           return (do_pop3_ntlm(sock, ctl, 1) == 0) ? PS_SUCCESS : PS_AUTHFAIL;
+       if (ctl->server.authenticate == A_NTLM || (has_ntlm && ctl->server.authenticate == A_ANY)) {
+           ok = do_pop3_ntlm(sock, ctl, 0);
+           if (ok == 0 || ctl->server.authenticate != A_ANY)
+               break;
+       }
 #else
-    if (ctl->server.authenticate == A_NTLM || ctl->server.authenticate == A_MSN)
-    {
-       report(stderr,
-          GT_("Required NTLM capability not compiled into fetchmail\n"));
-    }
+       if (ctl->server.authenticate == A_NTLM || ctl->server.authenticate == A_MSN)
+       {
+           report(stderr,
+                   GT_("Required NTLM capability not compiled into fetchmail\n"));
+       }
 #endif
 
-       if (ctl->server.authenticate == A_CRAM_MD5 || 
-           (has_cram && ctl->server.authenticate == A_ANY))
+       if (ctl->server.authenticate == A_CRAM_MD5 ||
+               (has_cram && ctl->server.authenticate == A_ANY))
        {
            ok = do_cram_md5(sock, "AUTH", ctl, NULL);
            if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
                break;
        }
 
+       if (ctl->server.authenticate == A_APOP
+                   || ctl->server.authenticate == A_ANY)
+       {
+           ok = do_apop(sock, ctl, greeting);
+           if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
+               break;
+       }
+
        /* ordinary validation, no one-time password or RPA */ 
        if ((ok = gen_transact(sock, "USER %s", ctl->remotename)))
            break;
@@ -599,52 +657,6 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
        shroud[0] = '\0';
        break;
 
-    case P_APOP:
-       /* build MD5 digest from greeting timestamp + password */
-       /* find start of timestamp */
-       for (start = greeting;  *start != 0 && *start != '<';  start++)
-           continue;
-       if (*start == 0) {
-           report(stderr,
-                  GT_("Required APOP timestamp not found in greeting\n"));
-           return(PS_AUTHFAIL);
-       }
-
-       /* find end of timestamp */
-       for (end = start;  *end != 0  && *end != '>';  end++)
-           continue;
-       if (*end == 0 || end == start + 1) {
-           report(stderr, 
-                  GT_("Timestamp syntax error in greeting\n"));
-           return(PS_AUTHFAIL);
-       }
-       else
-           *++end = '\0';
-
-       /* SECURITY: 2007-03-17
-        * Strictly validating the presented challenge for RFC-822
-        * conformity (it must be a msg-id in terms of that standard) is
-        * supposed to make attacks against the MD5 implementation
-        * harder[1]
-        *
-        * [1] "Security vulnerability in APOP authentication",
-        *     Gaëtan Leurent, fetchmail-devel, 2007-03-17 */
-       if (!rfc822_valid_msgid((unsigned char *)start)) {
-           report(stderr,
-                   GT_("Invalid APOP timestamp.\n"));
-           return PS_AUTHFAIL;
-       }
-
-       /* copy timestamp and password into digestion buffer */
-       msg = (char *)xmalloc((end-start+1) + strlen(ctl->password) + 1);
-       strcpy(msg,start);
-       strcat(msg,ctl->password);
-       strcpy((char *)ctl->digest, MD5Digest((unsigned char *)msg));
-       free(msg);
-
-       ok = gen_transact(sock, "APOP %s %s", ctl->remotename, (char *)ctl->digest);
-       break;
-
     default:
        report(stderr, GT_("Undefined protocol request in POP3_auth\n"));
        ok = PS_ERROR;
index 7310f6b91f3be6ea8716a9f65b06dd67dc6d86e6..7cbf12ed3c9beaedceb93cd161a85e192204b9f2 100644 (file)
@@ -89,6 +89,7 @@ cram(-md5)?   { SETSTATE(0); yylval.proto = A_CRAM_MD5; return AUTHTYPE;}
 msn            { SETSTATE(0); yylval.proto = A_MSN; return AUTHTYPE;}
 ntlm           { SETSTATE(0); yylval.proto = A_NTLM; return AUTHTYPE;}
 <AUTH>password { SETSTATE(0); yylval.proto = A_PASSWORD; return AUTHTYPE;}
+apop           { SETSTATE(0); yylval.proto = A_APOP; return AUTHTYPE;}
 timeout                { return TIMEOUT;}
 envelope       { return ENVELOPE; }
 qvirtual       { return QVIRTUAL; }
@@ -195,12 +196,11 @@ options           {/* EMPTY */}
 [;:,]          {/* EMPTY */}
 
 (auto)|(AUTO)  { yylval.proto = P_AUTO;  return PROTO; }
-(sdps)|(SDPS)   { return SDPS; }
+(sdps)|(SDPS)  { return SDPS; }
 (pop3)|(POP3)  { yylval.proto = P_POP3;  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; }
-(odmr)|(ODMR)   { yylval.proto = P_ODMR;  return PROTO; }
+(etrn)|(ETRN)  { yylval.proto = P_ETRN;  return PROTO; }
+(odmr)|(ODMR)  { yylval.proto = P_ODMR;  return PROTO; }
 (kpop)|(KPOP)  { return KPOP; }
 
 (#.*)?\\?\n    { prc_lineno++; }   /* newline is ignored */