File fetchmail-CVE-2021-39272.patch of Package fetchmail.21478
Index: fetchmail-6.3.26/base64.c
===================================================================
--- fetchmail-6.3.26.orig/base64.c
+++ fetchmail-6.3.26/base64.c
@@ -27,6 +27,11 @@ static const char base64val[] = {
};
#define DECODE64(c) (isascii((unsigned char)(c)) ? base64val[c] : BAD)
+unsigned len64frombits(unsigned inlen)
+{
+ return (inlen + 2)/3*4;
+}
+
void to64frombits(char *out, const void *in_, int inlen)
/* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
{
Index: fetchmail-6.3.26/driver.c
===================================================================
--- fetchmail-6.3.26.orig/driver.c
+++ fetchmail-6.3.26/driver.c
@@ -865,6 +865,7 @@ static int do_session(
SIGHANDLERTYPE alrmsave;
ctl->server.base_protocol = proto;
+ stage = STAGE_GETAUTH;
msgsizes = NULL;
pass = 0;
@@ -964,6 +965,12 @@ static int do_session(
goto closeUp;
}
+ /* initialize protocol */
+ if (ctl->server.base_protocol->construct) {
+ err = (ctl->server.base_protocol->construct)(ctl);
+ if (err) goto cleanUp;
+ }
+
/* open a socket to the mail server */
oldphase = phase;
phase = OPEN_WAIT;
@@ -1152,7 +1159,6 @@ static int do_session(
goto cleanUp;
/* try to get authorized to fetch mail */
- stage = STAGE_GETAUTH;
if (ctl->server.base_protocol->getauth)
{
set_timeout(mytimeout);
@@ -1242,10 +1248,18 @@ is restored."));
ctl->remotename,
ctl->server.truename);
}
+ else if (err == PS_SOCKET)
+ {
+ report(stderr, GT_("Socket or TLS error on %s@%s\n"),
+ ctl->remotename,
+ ctl->server.truename);
+ }
else
+ {
report(stderr, GT_("Unknown login or authentication error on %s@%s\n"),
ctl->remotename,
ctl->server.truename);
+ }
goto cleanUp;
}
@@ -1538,6 +1552,11 @@ is restored."));
cleanupSockClose(mailserver_socket_temp);
mailserver_socket_temp = -1;
}
+
+ /* clean up protocol */
+ if (ctl->server.base_protocol->destruct) {
+ ctl->server.base_protocol->destruct(ctl);
+ }
}
/* no report on PS_AUTHFAIL */
Index: fetchmail-6.3.26/etrn.c
===================================================================
--- fetchmail-6.3.26.orig/etrn.c
+++ fetchmail-6.3.26/etrn.c
@@ -143,6 +143,8 @@ static const struct method etrn =
NULL, /* no mailbox support */
etrn_logout, /* log out, we're done */
FALSE, /* no, we can't re-poll */
+ NULL, /* no constructor */
+ NULL /* no destructor */
};
int doETRN (struct query *ctl)
Index: fetchmail-6.3.26/fetchmail.c
===================================================================
--- fetchmail-6.3.26.orig/fetchmail.c
+++ fetchmail-6.3.26/fetchmail.c
@@ -722,7 +722,7 @@ int main(int argc, char **argv)
/* Boldly assume that we also have res_init() if we have
* res_search(), and call res_init() to re-read the resolv.conf
* file, so that we can pick up changes to that file that are
- * written by dhpccd, dhclient, pppd, openvpn and similar. */
+ * written by dhcpcd, dhclient, pppd, openvpn and similar. */
/* NOTE: This assumes that /etc/resolv.conf is written
* atomically (i. e. a temporary file is written, flushed and
@@ -1356,6 +1356,16 @@ static int load_params(int argc, char **
ctl->server.pollname);
exit(PS_SYNTAX);
}
+ switch (ctl->server.protocol) {
+ case P_POP3: case P_APOP:
+ if (port == 995 && !ctl->use_ssl) report(stderr, GT_("WARNING: %s configuration invalid, you normally need --ssl for port 995/service pop3s.\n"), ctl->server.pollname);
+ if (port == 110 && ctl->use_ssl) report(stderr, GT_("WARNING: %s configuration invalid, you normally need port 995/service pop3s for --ssl.\n"), ctl->server.pollname);
+ break;
+ case P_IMAP:
+ if (port == 993 && !ctl->use_ssl) report(stderr, GT_("WARNING: %s configuration invalid, you normally need --ssl for port 993/service imaps.\n"), ctl->server.pollname);
+ if (port == 143 && ctl->use_ssl) report(stderr, GT_("WARNING: %s configuration invalid, you normally need port 993/service imaps for --ssl.\n"), ctl->server.pollname);
+ break;
+ }
}
if (ctl->listener == LMTP_MODE)
{
Index: fetchmail-6.3.26/fetchmail.h
===================================================================
--- fetchmail-6.3.26.orig/fetchmail.h
+++ fetchmail-6.3.26/fetchmail.h
@@ -254,6 +254,8 @@ struct method /* describe methods for p
int (*logout_cmd)(int, struct query *);
/* logout command */
flag retry; /* can getrange poll for new messages? */
+ int (*construct)(struct query *); /* session setup before first command */
+ int (*destruct)(struct query *); /* cleanup after session */
};
enum badheader { BHREJECT = 0, BHACCEPT };
@@ -270,7 +272,7 @@ struct hostdata /* shared among all use
int interval; /* # cycles to skip between polls */
int authenticate; /* authentication mode to try */
int timeout; /* inactivity timout in seconds */
- char *envelope; /* envelope address list header */
+ char *envelope; /* envelope address list header - WARNING - can take value STRING_DISABLED (-1)! */
int envskip; /* skip to numbered envelope header */
char *qvirtual; /* prefix removed from local user id */
flag skip; /* suppress poll in implicit mode? */
@@ -636,6 +638,7 @@ int prc_parse_file(const char *, const f
int prc_filecheck(const char *, const flag);
/* base64.c */
+unsigned len64frombits(unsigned inlen); /** calculate length needed to encode inlen octets. warnings: 1. caller needs to add 1 for a trailing \0 byte himself. 2. returns 0 for inlen 0! */
void to64frombits(char *, const void *, int);
int from64tobits(void *, const char *, int maxlen);
Index: fetchmail-6.3.26/fetchmail.man
===================================================================
--- fetchmail-6.3.26.orig/fetchmail.man
+++ fetchmail-6.3.26/fetchmail.man
@@ -69,7 +69,12 @@ language (if supported). However if you
please leave it in. The maintainers do not necessarily understand your
language, please use English.
-
+.SH TLS (SSL) QUICKSTART
+.PP
+Your fetchmail distribution should have come with a README.SSL file, which see.
+It is recommended to configure all polls with --ssl --sslproto tls1.2+
+if supported by the server, which configures fetchmail along recent IETF
+proposed standards and best current practices, RFC-8314, RFC-8996, RFC-8997.
.SS CONCEPTS
If \fBfetchmail\fP is used with a POP or an IMAP server (but not with
@@ -412,13 +417,11 @@ from. The folder information is written
.B \-\-ssl
(Keyword: ssl)
.br
-Causes the connection to the mail server to be encrypted
-via SSL. Connect to the server using the specified base protocol over a
-connection secured by SSL. This option defeats opportunistic starttls
-negotiation. It is highly recommended to use \-\-sslproto 'SSL3'
-\-\-sslcertck to validate the certificates presented by the server and
-defeat the obsolete SSLv2 negotiation. More information is available in
-the \fIREADME.SSL\fP file that ships with fetchmail.
+Causes the connection to the mail server to be encrypted via SSL, by
+negotiating SSL directly after connecting (called SSL-wrapped mode, or
+Implicit TLS by RFC-8314). Please see the description of \-\-sslproto
+below! More information is available in the \fIREADME.SSL\fP file that
+ships with fetchmail.
.IP
Note that fetchmail may still try to negotiate SSL through starttls even
if this option is omitted. You can use the \-\-sslproto option to defeat
Index: fetchmail-6.3.26/imap.c
===================================================================
--- fetchmail-6.3.26.orig/imap.c
+++ fetchmail-6.3.26/imap.c
@@ -34,9 +34,22 @@ static int preauth = FALSE;
/* session variables initialized in capa_probe() or imap_getauth() */
static char capabilities[MSGBUFSIZE+1];
static int imap_version = IMAP4;
-static flag do_idle = FALSE, has_idle = FALSE;
+static flag has_idle = FALSE;
static int expunge_period = 1;
+static void clear_sessiondata(void) {
+ /* must match defaults above */
+ preauth = FALSE;
+ memset(capabilities, 0, sizeof(capabilities));
+ imap_version = IMAP4;
+ has_idle = FALSE;
+ expunge_period = 1;
+}
+
+/* the next ones need to be kept in synch - C89 does not consider strlen()
+ * a const initializer */
+const char *const capa_begin = " [CAPABILITY "; const unsigned capa_len = 13;
+
/* mailbox variables initialized in imap_getrange() */
static int count = 0, oldcount = 0, recentcount = 0, unseen = 0, deletions = 0;
static unsigned int startcount = 1;
@@ -50,6 +63,56 @@ static int actual_deletions = 0;
static int saved_timeout = 0, idle_timeout = 0;
static time_t idle_start_time = 0;
+static int imap_setup(struct query *ctl)
+{
+ (void)ctl;
+ clear_sessiondata();
+ return PS_SUCCESS;
+}
+
+static int imap_cleanup(struct query *ctl)
+{
+ (void)ctl;
+ clear_sessiondata();
+ return PS_SUCCESS;
+}
+
+static void copy_capabilities(const char *buf)
+{
+ strlcpy(capabilities, buf, sizeof(capabilities));
+ capabilities[strcspn(capabilities, "]")] = '\0'; /* truncate at ] */
+
+ /* UW-IMAP server 10.173 notifies in all caps, but RFC2060 says we
+ should expect a response in mixed-case */
+ if (strstr(capabilities, "IMAP4REV1")) {
+ imap_version = IMAP4rev1; /* RFC-3501 (2060) */
+ if (outlevel >= O_DEBUG)
+ report(stdout, GT_("Protocol identified as IMAP4 rev 1\n"));
+ } else if (strstr(capabilities, "IMAP4")) {
+ imap_version = IMAP4; /* RFC-1730 */
+ if (outlevel >= O_DEBUG)
+ report(stdout, GT_("Protocol identified as IMAP4 rev 0\n"));
+ } else {
+ imap_version = IMAP2;
+ if (outlevel >= O_DEBUG)
+ report(stdout, GT_("Protocol identified as IMAP2 or IMAP2BIS\n"));
+ }
+
+ /*
+ * Handle idling. We depend on coming through here on startup
+ * and after each timeout (including timeouts during idles).
+ */
+ if (strstr(capabilities, "IDLE"))
+ has_idle = TRUE;
+ else
+ has_idle = FALSE;
+ if (outlevel >= O_VERBOSE)
+ report(stdout, GT_("will idle after poll\n")); /* FIXME: rename this to can... idle for next release */
+
+ peek_capable = (imap_version >= IMAP4);
+}
+
+
static int imap_untagged_response(int sock, const char *buf)
/* interpret untagged status responses */
{
@@ -58,7 +121,7 @@ static int imap_untagged_response(int so
if (stage == STAGE_GETAUTH
&& !strncmp(buf, "* CAPABILITY", 12))
{
- strlcpy(capabilities, buf + 12, sizeof(capabilities));
+ copy_capabilities(buf + 12);
}
else if (stage == STAGE_GETAUTH
&& !strncmp(buf, "* PREAUTH", 9))
@@ -183,6 +246,7 @@ static int imap_response(int sock, char
/* parse command response */
{
char buf[MSGBUFSIZE+1];
+ char *tmp;
do {
int ok;
@@ -200,6 +264,9 @@ static int imap_response(int sock, char
if (islower((unsigned char)*cp))
*cp = toupper((unsigned char)*cp);
+ /* FIXME: does not look for or handle command continuation requests,
+ * i. e. the token "+" instead of tag or "*" */
+
/* untagged responses start with "* " */
if (buf[0] == '*' && buf[1] == ' ') {
ok = imap_untagged_response(sock, buf);
@@ -219,6 +286,19 @@ static int imap_response(int sock, char
return(ok);
}
+ /* on login, the server may volunteer new CAPABILITY information
+ * with the tagged OK response, record it */
+ /* WARNING: this must match with what's in imap_getauth()! */
+ if (stage == STAGE_GETAUTH
+ && (tmp = strstr(buf, capa_begin)))
+ {
+ copy_capabilities(tmp + capa_len);
+
+ if (outlevel >= O_DEBUG) {
+ report(stdout, GT_("found updated capabilities list\n"));
+ }
+ }
+
if (stage == STAGE_IDLE)
{
/* reduce the timeout: servers may not reset their timeout
@@ -327,118 +407,89 @@ static void imap_canonicalize(char *resu
static int capa_probe(int sock, struct query *ctl)
/* set capability variables from a CAPA probe */
{
- int ok;
+ int err;
- /* probe to see if we're running IMAP4 and can use RFC822.PEEK */
- capabilities[0] = '\0';
- if ((ok = gen_transact(sock, "CAPABILITY")) == PS_SUCCESS)
- {
- char *cp;
-
- /* capability checks are supposed to be caseblind */
- for (cp = capabilities; *cp; cp++)
- *cp = toupper((unsigned char)*cp);
-
- /* UW-IMAP server 10.173 notifies in all caps, but RFC2060 says we
- should expect a response in mixed-case */
- if (strstr(capabilities, "IMAP4REV1"))
- {
- imap_version = IMAP4rev1;
- if (outlevel >= O_DEBUG)
- report(stdout, GT_("Protocol identified as IMAP4 rev 1\n"));
- }
- else
- {
- imap_version = IMAP4;
- if (outlevel >= O_DEBUG)
- report(stdout, GT_("Protocol identified as IMAP4 rev 0\n"));
- }
- }
- else if (ok == PS_ERROR)
- {
- imap_version = IMAP2;
- if (outlevel >= O_DEBUG)
- report(stdout, GT_("Protocol identified as IMAP2 or IMAP2BIS\n"));
- }
- else
- return ok;
+ (void)ctl;
- /*
- * Handle idling. We depend on coming through here on startup
- * and after each timeout (including timeouts during idles).
- */
- do_idle = ctl->idle;
- if (ctl->idle)
- {
- if (strstr(capabilities, "IDLE"))
- has_idle = TRUE;
- else
- has_idle = FALSE;
- if (outlevel >= O_VERBOSE)
- report(stdout, GT_("will idle after poll\n"));
+ /* probe to see if we're running IMAP4 and can use RFC822.PEEK */
+ memset(capabilities, 0, sizeof capabilities);
+ err = gen_transact(sock, "CAPABILITY");
+ /* if successful, copy_capabilities() will have handled it */
+ if (err == PS_ERROR) {
+ /* this is OK for IMAP2 which did not support a CAPABILITY command */
+ err = PS_SUCCESS;
}
- peek_capable = (imap_version >= IMAP4);
-
- return PS_SUCCESS;
+ return err;
}
-static int do_authcert (int sock, const char *command, const char *name)
+static int do_auth_external (int sock, const char *command, const char *name)
/* do authentication "external" (authentication provided by client cert) */
{
+ /* FIXME: not compliant with RFC 4422 (SASL) without RFC 4959 (SASL-IR)-
+ * does not support the usual server challenge/response
+ */
char buf[256];
+ if (!strstr(capabilities, "SASL-IR")) {
+ report(stderr, GT_("server did not advertise SASL-IR extension but fetchmail's implementation requires it for AUTHENTICATE EXTERNAL\n"));
+ return PS_AUTHFAIL;
+ }
+
if (name && name[0])
{
size_t len = strlen(name);
- if ((len / 3) + ((len % 3) ? 4 : 0) < sizeof(buf))
+ if (len64frombits(len) + 1 <= sizeof(buf)) /* +1: need to fit \0 byte */
to64frombits (buf, name, strlen(name));
else
return PS_AUTHFAIL; /* buffer too small. */
}
else
- buf[0]=0;
+ {
+ strcpy(buf, "=");
+ }
return gen_transact(sock, "%s EXTERNAL %s",command,buf);
}
+/** apply for connection authorization, possibly executing STARTTLS */
static int imap_getauth(int sock, struct query *ctl, char *greeting)
-/* apply for connection authorization */
{
int ok = 0;
- (void)greeting;
+ char *commonname;
/*
* Assumption: expunges are cheap, so we want to do them
* after every message unless user said otherwise.
*/
if (NUM_SPECIFIED(ctl->expunge))
- expunge_period = NUM_VALUE_OUT(ctl->expunge);
+ expunge_period = NUM_VALUE_OUT(ctl->expunge);
else
- expunge_period = 1;
-
- if ((ok = capa_probe(sock, ctl)))
- return ok;
+ expunge_period = 1;
- /*
- * If either (a) we saw a PREAUTH token in the greeting, or
- * (b) the user specified ssh preauthentication, then we're done.
- */
- if (preauth || ctl->server.authenticate == A_SSH)
- {
- preauth = FALSE; /* reset for the next session */
- return(PS_SUCCESS);
+ /* check if imap_ok() has already parsed CAPABILITY from the greeting when
+ * driver.c ran it on the server's greeting message - note this must match
+ * with what's in imap_response()! */
+ if (!strstr(greeting, capa_begin)) {
+ int err = capa_probe(sock, ctl);
+ if (err) return err;
}
-#ifdef SSL_ENABLE
- if (maybe_tls(ctl)) {
- char *commonname;
-
commonname = ctl->server.pollname;
if (ctl->server.via)
commonname = ctl->server.via;
if (ctl->sslcommonname)
commonname = ctl->sslcommonname;
+#ifdef SSL_ENABLE
+ /* Defend against a PREAUTH-prevents-STARTTLS attack */
+ if (preauth && must_tls(ctl)) {
+ report(stderr, GT_("%s: configuration requires TLS, but STARTTLS is not permitted "
+ "because of authenticated state (PREAUTH). Aborting connection. Server permitting, try --ssl instead (see manual).\n"), commonname);
+ preauth = FALSE; /* reset for the next session */
+ return PS_SOCKET;
+ }
+
+ if (maybe_tls(ctl)) {
if (strstr(capabilities, "STARTTLS")
|| must_tls(ctl)) /* if TLS is mandatory, ignore capabilities */
{
@@ -451,6 +502,11 @@ static int imap_getauth(int sock, struct
ctl->sslcertfile, ctl->sslcertpath, ctl->sslfingerprint, commonname,
ctl->server.pollname, &ctl->remotename)) != -1)
{
+ if (outlevel >= O_VERBOSE)
+ {
+ report(stdout, GT_("%s: upgrade to TLS succeeded.\n"), commonname);
+ }
+
/*
* RFC 2595 says this:
*
@@ -464,11 +520,10 @@ static int imap_getauth(int sock, struct
* Now that we're confident in our TLS connection we can
* guarantee a secure capability re-probe.
*/
+ clear_sessiondata();
if ((ok = capa_probe(sock, ctl)))
- return ok;
- if (outlevel >= O_VERBOSE)
{
- report(stdout, GT_("%s: upgrade to TLS succeeded.\n"), commonname);
+ return ok;
}
} else if (must_tls(ctl)) {
/* Config required TLS but we couldn't guarantee it, so we must
@@ -496,10 +551,23 @@ static int imap_getauth(int sock, struct
#endif /* SSL_ENABLE */
/*
+ * If either (a) we saw a PREAUTH token in the greeting, or
+ * (b) the user specified ssh preauthentication, then we're done.
+ */
+ if (preauth || ctl->server.authenticate == A_SSH)
+ {
+ preauth = FALSE; /* reset for the next session */
+ return(PS_SUCCESS);
+ }
+
+ /*
* Time to authenticate the user.
* Try the protocol variants that don't require passwords first.
*/
- ok = PS_AUTHFAIL;
+ ok = PS_AUTHFAIL; /* formally, never read,
+ but let's leave this in place as a safe default
+ for future maintenance */
+ (void)ok;
/* Yahoo hack - we'll just try ID if it was offered by the server,
* and IGNORE errors. */
@@ -510,19 +578,23 @@ static int imap_getauth(int sock, struct
}
}
- if ((ctl->server.authenticate == A_ANY
+ if (ctl->server.authenticate == A_ANY
|| ctl->server.authenticate == A_EXTERNAL)
- && strstr(capabilities, "AUTH=EXTERNAL"))
{
- ok = do_authcert(sock, "AUTHENTICATE", ctl->remotename);
- if (ok)
- {
- /* SASL cancellation of authentication */
- gen_send(sock, "*");
- if (ctl->server.authenticate != A_ANY)
- return ok;
- } else {
- return ok;
+ if (!strstr(capabilities, "AUTH=EXTERNAL")) {
+ if (ctl->server.authenticate == A_EXTERNAL) {
+ report(stderr, GT_("%s: --auth external requested but server does not advertise it.\n"), commonname);
+ return PS_AUTHFAIL;
+ }
+ } else {
+ int err = do_auth_external(sock, "AUTHENTICATE", ctl->remotename);
+ if (err)
+ {
+ if (ctl->server.authenticate != A_ANY)
+ return err;
+ } else {
+ return PS_SUCCESS;
+ }
}
}
@@ -630,42 +702,44 @@ static int imap_getauth(int sock, struct
/*
* We're stuck with sending the password en clair.
- * The reason for this odd-looking logic is that some
- * servers return LOGINDISABLED even though login
- * actually works. So arrange things in such a way that
- * setting auth passwd makes it ignore this capability.
- */
- if((ctl->server.authenticate==A_ANY&&!strstr(capabilities,"LOGINDISABLED"))
- || ctl->server.authenticate == A_PASSWORD)
- {
- /* these sizes guarantee no buffer overflow */
- char *remotename, *password;
- size_t rnl, pwl;
- rnl = 2 * strlen(ctl->remotename) + 1;
- pwl = 2 * strlen(ctl->password) + 1;
- remotename = (char *)xmalloc(rnl);
- password = (char *)xmalloc(pwl);
-
- imap_canonicalize(remotename, ctl->remotename, rnl);
- imap_canonicalize(password, ctl->password, pwl);
-
- snprintf(shroud, sizeof (shroud), "\"%s\"", password);
- ok = gen_transact(sock, "LOGIN \"%s\" \"%s\"", remotename, password);
- memset(shroud, 0x55, sizeof(shroud));
- shroud[0] = '\0';
- memset(password, 0x55, strlen(password));
- free(password);
- free(remotename);
- if (ok)
- {
- if(ctl->server.authenticate != A_ANY)
- return ok;
+ * Older fetchmail versions permitted overriding LOGINDISABLED, documenting
+ * that it still works on some servers, but 6.4.22 disables this. */
+ if (ctl->server.authenticate == A_ANY
+ || ctl->server.authenticate == A_PASSWORD)
+ {
+ if (strstr(capabilities, "LOGINDISABLED")) {
+ if (ctl->server.authenticate == A_PASSWORD) {
+ report(stderr, GT_("%s: --auth password requested but server forbids it (LOGINDISABLED).\n"), commonname);
+ return PS_AUTHFAIL;
+ }
+ } else {
+ /* these sizes guarantee no buffer overflow */
+ static char *remotename, *password; /* XXX FIXME: not thread-safe but dynamic buffer is leaky on timeout */
+ size_t rnl, pwl;
+ rnl = 2 * strlen(ctl->remotename) + 1;
+ pwl = 2 * strlen(ctl->password) + 1;
+ if (remotename) xfree(remotename);
+ remotename = (char *)xmalloc(rnl);
+ if (password) xfree(password);
+ password = (char *)xmalloc(pwl);
+
+ imap_canonicalize(remotename, ctl->remotename, rnl);
+ imap_canonicalize(password, ctl->password, pwl);
+
+ snprintf(shroud, sizeof (shroud), "\"%s\"", password);
+ ok = gen_transact(sock, "LOGIN \"%s\" \"%s\"", remotename, password);
+ memset(shroud, 0x55, sizeof(shroud));
+ shroud[0] = '\0';
+ memset(password, 0x55, strlen(password));
+ xfree(password);
+ xfree(remotename);
+ return ok; /* this assumes that password is the last authentication method to try */
}
- else
- return(ok);
}
- return(ok);
+ /* if we're here, we've run out of authentication methods */
+ report(stderr, GT_("%s: we've run out of authentication methods and cannot log in.\n"), commonname);
+ return PS_AUTHFAIL;
}
static int internal_expunge(int sock)
@@ -895,7 +969,7 @@ static int imap_getrange(int sock,
*
* this is a while loop because imap_idle() might return on other
* mailbox changes also */
- while (recentcount == 0 && do_idle) {
+ while (recentcount == 0 && ctl->idle && has_idle) {
smtp_close(ctl, 1);
ok = imap_idle(sock);
if (ok)
@@ -952,7 +1026,7 @@ static int imap_getrange(int sock,
count), count);
}
- if (count == 0 && do_idle)
+ if (count == 0 && ctl->idle && has_idle)
{
/* no messages? then we may need to idle until we get some */
while (count == 0) {
@@ -1407,6 +1481,8 @@ static const struct method imap =
imap_end_mailbox_poll, /* end-of-mailbox processing */
imap_logout, /* expunge and exit */
TRUE, /* yes, we can re-poll */
+ imap_setup, /* setup method */
+ imap_cleanup /* cleanup method */
};
int doIMAP(struct query *ctl)
Index: fetchmail-6.3.26/lock.c
===================================================================
--- fetchmail-6.3.26.orig/lock.c
+++ fetchmail-6.3.26/lock.c
@@ -59,8 +59,11 @@ void fm_lock_setup(struct runctl *ctl)
static void unlockit(void)
/* must-do actions for exit (but we can't count on being able to do malloc) */
{
- if (lockfile && lock_acquired)
- unlink(lockfile);
+ if (lockfile && lock_acquired) {
+ if (unlink(lockfile)) {
+ (void)truncate(lockfile, (off_t)0);
+ }
+ }
}
void fm_lock_dispose(void)
Index: fetchmail-6.3.26/odmr.c
===================================================================
--- fetchmail-6.3.26.orig/odmr.c
+++ fetchmail-6.3.26/odmr.c
@@ -234,6 +234,8 @@ static const struct method odmr =
NULL, /* no mailbox support */
odmr_logout, /* log out, we're done */
FALSE, /* no, we can't re-poll */
+ NULL, /* no constructor */
+ NULL /* no destructor */
};
int doODMR (struct query *ctl)
Index: fetchmail-6.3.26/pop2.c
===================================================================
--- fetchmail-6.3.26.orig/pop2.c
+++ fetchmail-6.3.26/pop2.c
@@ -171,7 +171,9 @@ static const struct method pop2 =
NULL, /* how to mark a message as seen */
NULL, /* how to end mailbox processing */
pop2_logout, /* log out, we're done */
- FALSE /* no, we can't re-poll */
+ FALSE, /* no, we can't re-poll */
+ NULL, /* no constructor */
+ NULL /* no destructor */
};
int doPOP2 (struct query *ctl)
Index: fetchmail-6.3.26/pop3.c
===================================================================
--- fetchmail-6.3.26.orig/pop3.c
+++ fetchmail-6.3.26/pop3.c
@@ -41,23 +41,29 @@ static char lastok[POPBUFSIZE+1];
#endif /* OPIE_ENABLE */
/* session variables initialized in capa_probe() or pop3_getauth() */
-flag done_capa = FALSE;
-#if defined(GSSAPI)
-flag has_gssapi = FALSE;
-#endif /* defined(GSSAPI) */
-#if defined(KERBEROS_V4) || defined(KERBEROS_V5)
-flag has_kerberos = FALSE;
-#endif /* defined(KERBEROS_V4) || defined(KERBEROS_V5) */
+/* some of these will not be accessed depending on fetchmail's
+ * compile-time configuration */
+static flag done_capa = FALSE;
+static flag has_gssapi = FALSE;
+static flag has_kerberos = FALSE;
static flag has_cram = FALSE;
-#ifdef OPIE_ENABLE
-flag has_otp = FALSE;
-#endif /* OPIE_ENABLE */
-#ifdef NTLM_ENABLE
-flag has_ntlm = FALSE;
-#endif /* NTLM_ENABLE */
-#ifdef SSL_ENABLE
+static flag has_otp = FALSE;
+static flag has_ntlm = FALSE;
static flag has_stls = FALSE;
-#endif /* SSL_ENABLE */
+
+static void clear_sessiondata(void) {
+ /* must match defaults above */
+#ifdef OPIE_ENABLE
+ memset(lastok, 0, sizeof(lastok));
+#endif
+ done_capa = FALSE;
+ has_gssapi = FALSE;
+ has_kerberos = FALSE;
+ has_cram = FALSE;
+ has_otp = FALSE;
+ has_ntlm = FALSE;
+ has_stls = FALSE;
+}
/* mailbox variables initialized in pop3_getrange() */
static int last;
@@ -106,6 +112,20 @@ static int do_pop3_ntlm(int sock, struct
#define DOTLINE(s) (s[0] == '.' && (s[1]=='\r'||s[1]=='\n'||s[1]=='\0'))
+static int pop3_setup(struct query *ctl)
+{
+ (void)ctl;
+ clear_sessiondata();
+ return PS_SUCCESS;
+}
+
+static int pop3_cleanup(struct query *ctl)
+{
+ (void)ctl;
+ clear_sessiondata();
+ return PS_SUCCESS;
+}
+
static int pop3_ok (int sock, char *argbuf)
/* parse command response */
{
@@ -129,7 +149,7 @@ static int pop3_ok (int sock, char *argb
if (strcmp(buf,"+OK") == 0)
{
#ifdef OPIE_ENABLE
- strcpy(lastok, bufp);
+ strlcpy(lastok, bufp, sizeof(lastok));
#endif /* OPIE_ENABLE */
ok = 0;
}
@@ -220,6 +240,7 @@ static int capa_probe(int sock)
if (ok == PS_SUCCESS)
{
char buffer[64];
+ char *cp;
/* determine what authentication methods we have available */
while ((ok = gen_recv(sock, buffer, sizeof(buffer))) == 0)
@@ -227,6 +248,8 @@ static int capa_probe(int sock)
if (DOTLINE(buffer))
break;
+ for (cp = buffer; *cp; cp++) *cp = toupper((unsigned char)*cp);
+
#ifdef SSL_ENABLE
if (strstr(buffer, "STLS"))
has_stls = TRUE;
@@ -283,21 +306,6 @@ static int pop3_getauth(int sock, struct
flag connection_may_have_tls_errors = FALSE;
#endif /* SSL_ENABLE */
- done_capa = FALSE;
-#if defined(GSSAPI)
- has_gssapi = FALSE;
-#endif /* defined(GSSAPI) */
-#if defined(KERBEROS_V4) || defined(KERBEROS_V5)
- has_kerberos = FALSE;
-#endif /* defined(KERBEROS_V4) || defined(KERBEROS_V5) */
- has_cram = FALSE;
-#ifdef OPIE_ENABLE
- has_otp = FALSE;
-#endif /* OPIE_ENABLE */
-#ifdef SSL_ENABLE
- has_stls = FALSE;
-#endif /* SSL_ENABLE */
-
/* Set this up before authentication quits early. */
set_peek_capable(ctl);
@@ -329,9 +337,6 @@ static int pop3_getauth(int sock, struct
}
peek_capable = 0;
}
- if (ctl->server.authenticate == A_SSH) {
- return PS_SUCCESS;
- }
#ifdef SDPS_ENABLE
/*
@@ -345,33 +350,6 @@ static int pop3_getauth(int sock, struct
switch (ctl->server.protocol) {
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 */
- if (strstr(ctl->remotename, "@compuserve.com"))
- {
- /* AUTH command should return a list of available mechanisms */
- if (gen_transact(sock, "AUTH") == 0)
- {
- char buffer[10];
- flag has_rpa = FALSE;
-
- while ((ok = gen_recv(sock, buffer, sizeof(buffer))) == 0)
- {
- if (DOTLINE(buffer))
- break;
- if (strncasecmp(buffer, "rpa", 3) == 0)
- has_rpa = TRUE;
- }
- if (has_rpa && !POP3_auth_rpa(ctl->remotename,
- ctl->password, sock))
- return(PS_SUCCESS);
- }
-
- return(PS_AUTHFAIL);
- }
-#endif /* RPA_ENABLE */
-
/*
* CAPA command may return a list including available
* authentication mechanisms and STLS capability.
@@ -408,8 +386,8 @@ static int pop3_getauth(int sock, struct
#ifdef SSL_ENABLE
if (must_tls(ctl)) {
/* fail with mandatory STLS without repoll */
- report(stderr, GT_("TLS is mandatory for this session, but server refused CAPA command.\n"));
- report(stderr, GT_("The CAPA command is however necessary for TLS.\n"));
+ report(stderr, GT_("STLS is mandatory for this session, but server refused CAPA command.\n"));
+ report(stderr, GT_("CAPA command support is, however, necessary for STLS.\n"));
return ok;
} else if (maybe_tls(ctl)) {
/* defeat opportunistic STLS */
@@ -466,15 +444,15 @@ static int pop3_getauth(int sock, struct
* guarantee a secure capability re-probe.
*/
set_timeout(0);
- done_capa = FALSE;
- ok = capa_probe(sock);
- if (ok != PS_SUCCESS) {
- return ok;
- }
if (outlevel >= O_VERBOSE)
{
report(stdout, GT_("%s: upgrade to TLS succeeded.\n"), commonname);
}
+ clear_sessiondata();
+ ok = capa_probe(sock);
+ if (ok != PS_SUCCESS) {
+ return ok;
+ }
} else if (must_tls(ctl)) {
/* Config required TLS but we couldn't guarantee it, so we must
* stop. */
@@ -498,6 +476,48 @@ static int pop3_getauth(int sock, struct
} /* maybe_tls() */
#endif /* SSL_ENABLE */
+ if (ctl->server.authenticate == A_SSH) {
+ return PS_SUCCESS;
+ }
+
+#ifdef RPA_ENABLE
+ /* XXX FIXME: AUTH probing (RFC1734) should become global */
+ /* CompuServe POP3 Servers as of 990730 want AUTH first for RPA */
+ if (strstr(ctl->remotename, "@compuserve.com")
+ && ctl->server.authenticate == A_ANY)
+ {
+ /* AUTH command should return a list of available mechanisms. */
+ /* 2021 update: it is unclear which software still supports RPA these days.
+ This behavior (AUTH without a method argument, to query)
+ is not sanctioned by RFC-1734 but was/is apparently
+ supported by Compuserve and Microsoft for their NTLM:
+ https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-pop3/cb6829e1-d6f4-447b-9092-2671a257563c
+ */
+ if (gen_transact(sock, "AUTH") == 0)
+ {
+ char buffer[10];
+ flag has_rpa = FALSE;
+ int err;
+
+ while ((err = gen_recv(sock, buffer, sizeof(buffer))) == 0)
+ {
+ if (DOTLINE(buffer))
+ break;
+ if (strncasecmp(buffer, "rpa", 3) == 0)
+ has_rpa = TRUE;
+ }
+ if (err) {
+ return err;
+ }
+ if (has_rpa && !POP3_auth_rpa(ctl->remotename,
+ ctl->password, sock))
+ return PS_SUCCESS;
+ }
+
+ return PS_AUTHFAIL;
+ }
+#endif /* RPA_ENABLE */
+
/*
* OK, we have an authentication type now.
*/
@@ -556,6 +576,7 @@ static int pop3_getauth(int sock, struct
{
report(stderr,
GT_("Required NTLM capability not compiled into fetchmail\n"));
+ return PS_AUTHFAIL;
}
#endif
@@ -1414,6 +1435,8 @@ static const struct method pop3 =
NULL, /* no action at end of mailbox */
pop3_logout, /* log out, we're done */
FALSE, /* no, we can't re-poll */
+ pop3_setup, /* setup method */
+ pop3_cleanup /* cleanup method */
};
int doPOP3 (struct query *ctl)
Index: fetchmail-6.3.26/README.SSL-SERVER
===================================================================
--- fetchmail-6.3.26.orig/README.SSL-SERVER
+++ fetchmail-6.3.26/README.SSL-SERVER
@@ -9,6 +9,11 @@ In order to let any mail client (not jus
properly, so that users can be sure their connection is not eavesdropped, there
are several requirements that need to be fulfilled.
+0. Provide modern TLS implementations:
+
+ Make sure the server supports TLS 1.2 and 1.3.
+ Older versions are deprecated and may preclude modern clients.
+
1. Match certificate and DNS names:
The server certificate's "common name" or "subject alternative name" must
Index: fetchmail-6.3.26/report.c
===================================================================
--- fetchmail-6.3.26.orig/report.c
+++ fetchmail-6.3.26/report.c
@@ -268,7 +268,7 @@ report_build (FILE *errfp, message, va_a
rep_ensuresize(n + 1);
VA_START(args, message);
- n = report_vbuild(message, args);
+ (void)report_vbuild(message, args);
va_end(args);
#else
{
@@ -332,7 +332,7 @@ report_complete (FILE *errfp, message, v
rep_ensuresize(n + 1);
VA_START(args, message);
- n = report_vbuild(message, args);
+ (void)report_vbuild(message, args);
va_end(args);
#else
report_build(errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
Index: fetchmail-6.3.26/socket.c
===================================================================
--- fetchmail-6.3.26.orig/socket.c
+++ fetchmail-6.3.26/socket.c
@@ -81,7 +81,20 @@ extern int h_errno;
#endif /* ndef h_errno */
#ifdef HAVE_SOCKETPAIR
-static char *const *parse_plugin(const char *plugin, const char *host, const char *service)
+static void free_plugindata(char **argvec)
+{
+ if (argvec) {
+ xfree(*argvec);
+ xfree(argvec);
+ }
+}
+
+/** parse plugin and interpolate %h and %p with single-quoted host and service.
+ * Returns a malloc()ed pointer to a NULL-terminated vector of pointers, of
+ * which the first is also malloc()ed and the 2nd and later ones (if present)
+ * are pointers into the same memory region - these serve as input for the
+ * argument vector of execvp() in handle_plugin. */
+static char **parse_plugin(const char *plugin, const char *host, const char *service)
{
char **argvec;
const char *c, *p;
@@ -104,12 +117,7 @@ static char *const *parse_plugin(const c
}
plugin_copy_len = plugin_len + host_len * host_count + service_len * service_count;
- plugin_copy = (char *)malloc(plugin_copy_len + 1);
- if (!plugin_copy)
- {
- report(stderr, GT_("fetchmail: malloc failed\n"));
- return NULL;
- }
+ plugin_copy = (char *)xmalloc(plugin_copy_len + 1);
while (plugin_copy_offset < plugin_copy_len)
{ if ((plugin[plugin_offset] == '%') && (plugin[plugin_offset + 1] == 'h'))
@@ -138,6 +146,7 @@ static char *const *parse_plugin(const c
return NULL;
}
memset(argvec, 0, s);
+ argvec[0] = plugin_copy; /* make sure we can free() it in every case */
for (p = cp = plugin_copy, i = 0; *cp; cp++)
{ if ((!isspace((unsigned char)*cp)) && (cp == p ? 1 : isspace((unsigned char)*p))) {
argvec[i] = cp;
@@ -157,21 +166,29 @@ static int handle_plugin(const char *hos
/* get a socket mediated through a given external command */
{
int fds[2];
- char *const *argvec;
+ char **argvec;
/*
* The author of this code, Felix von Leitner <felix@convergence.de>, says:
* he chose socketpair() instead of pipe() because socketpair creates
* bidirectional sockets while allegedly some pipe() implementations don't.
*/
+ argvec = parse_plugin(plugin,host,service);
+ if (!argvec || !*argvec[0]) {
+ free_plugindata(argvec);
+ report(stderr, GT_("fetchmail: plugin for host %s service %s is empty, cannot run!\n"), host, service);
+ return -1;
+ }
if (socketpair(AF_UNIX,SOCK_STREAM,0,fds))
{
report(stderr, GT_("fetchmail: socketpair failed\n"));
+ free_plugindata(argvec);
return -1;
}
switch (fork()) {
case -1:
/* error */
+ free_plugindata(argvec);
report(stderr, GT_("fetchmail: fork failed\n"));
return -1;
case 0: /* child */
@@ -186,15 +203,12 @@ static int handle_plugin(const char *hos
(void) close(fds[0]);
if (outlevel >= O_VERBOSE)
report(stderr, GT_("running %s (host %s service %s)\n"), plugin, host, service);
- argvec = parse_plugin(plugin,host,service);
- if (argvec == NULL)
- _exit(EXIT_FAILURE);
execvp(*argvec, argvec);
report(stderr, GT_("execvp(%s) failed\n"), *argvec);
_exit(EXIT_FAILURE);
break;
default: /* parent */
- /* NOP */
+ free_plugindata(argvec);
break;
}
/* fds[0] is the child's end; close it for proper EOF detection */
Index: fetchmail-6.3.26/xmalloc.h
===================================================================
--- fetchmail-6.3.26.orig/xmalloc.h
+++ fetchmail-6.3.26/xmalloc.h
@@ -12,8 +12,12 @@
#define XMALLOCTYPE char
#endif
+#if !defined __GNUC__ || __GNUC__ < 2
+# define __attribute__(xyz) /* Ignore. */
+#endif
+
/** Allocate \a n characters of memory, abort program on failure. */
-XMALLOCTYPE *xmalloc(size_t n);
+XMALLOCTYPE *xmalloc(size_t n) __attribute__((malloc));
/** Reallocate \a n characters of memory, abort program on failure. */
XMALLOCTYPE *xrealloc(/*@null@*/ XMALLOCTYPE *, size_t n);
Index: fetchmail-6.3.26/README.SSL
===================================================================
--- fetchmail-6.3.26.orig/README.SSL
+++ fetchmail-6.3.26/README.SSL
@@ -8,12 +8,21 @@ Note: there is a separate document "READ
side requirements for proper SSL support. It has checklist-style and is not
specific to fetchmail.
-In case of troubles, mail the README.SSL-SERVER file to your ISP and
+In case of troubles, mail the README.SSL-SERVER file to your ISP and
have them check their server configuration against it.
-Unfortunately, fetchmail confuses SSL/TLS protocol levels with whether
-a service needs to use in-band negotiation (STLS/STARTTLS for POP3/IMAP4) or is
-totally SSL-wrapped on a separate port. For compatibility reasons, this cannot
+Note that fetchmail up to version 6.3.26 used to confuse SSL/TLS protocol
+levels with whether a service needs to use in-band negotiation (STLS/STARTTLS
+for POP3/IMAP4) or is totally SSL-wrapped ("Implicit TLS") on a separate port.
+Fetchmail 6.4 seeks to fix that to some extent without breaking the
+command-line and rcfile interfaces too much (see --ssl and --sslproto options,
+below and in the manual).
+
+fetchmail 6.4.0 will auto-negotiate TLSv1 or newer only.
+
+Unfortunately, fetchmail confuses SSL/TLS protocol levels with whether
+a service needs to use in-band negotiation (STLS/STARTTLS for POP3/IMAP4) or is
+totally SSL-wrapped on a separate port. For compatibility reasons, this cannot
be fixed in a bugfix release.
-- Matthias Andree, 2009-05-09
@@ -22,25 +31,44 @@ be fixed in a bugfix release.
Quickstart
----------
-For use of SSL or TLS with in-band negotiation on the regular service's port,
-i. e. with STLS or STARTTLS, use these command line options
+Use an up-to-date release of OpenSSL v1.1.1 or v3.0.0 or newer, so as to get
+TLSv1.3 support. Older OpenSSL versions are unsupported upstream, and
+fetchmail rejects versions before v1.0.2 and warns about versions before
+v1.1.1
+
+In all four examples below, the (--)sslcertck has become redundant
+since fetchmail v6.4.0, but since fetchmail 6.3 releases will be in circulation
+for too long, (--)sslcertck will remain in the examples below for now.
+
+For use of SSL or TLS on a separate port (recommended), called Implicit TLS,
+the whole TCP connection is SSL-encrypted from the very beginning (SSL- or
+TLS-wrapped), use these command line options (in the rcfile,
+omit all leading "--"):
- --sslproto tls1 --sslcertck
+ --ssl --sslproto tls1.2+ --sslcertck
or these options in the rcfile (after the respective "user"... options)
- sslproto tls1 sslcertck
+ ssl sslproto tls1.2+ sslcertck
+For use of SSL or TLS with in-band negotiation on the regular service's port,
+i. e. with STLS or STARTTLS, use these command line options (omitting --ssl or
+ssl):
-For use of SSL or TLS on a separate port, if the whole TCP connection is
-SSL-encrypted from the very beginning, use these command line options (in the
-rcfile, omit all leading "--"):
-
- --ssl --sslproto ssl3 --sslcertck
+ --sslproto tls1.2+ --sslcertck
or these options in the rcfile (after the respective "user"... options)
- ssl sslproto ssl3 sslcertck
+ sslproto tls1.2+ sslcertck
+
+With up to date OpenSSL libraries (1.1.1 or newer), and with recent
+software on the server, you can alternatively configure tls1.3+.
+
+For some older services, you may need to use tls1.1+ or tls1+ for compatibility
+instead of the tls1.2+ above. In such situations, you should ask the service
+provider or server operator to upgrade their TLS implementation such that
+TLS v1.3 be supported, and once that happens, update your fetchmail configuration
+to tls1.3+ or tls1.2+.
Background and use (long version :-))