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 :-))
openSUSE Build Service is sponsored by