#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) */
flag has_otp = FALSE;
#endif /* OPIE_ENABLE */
#ifdef SSL_ENABLE
-static flag has_ssl = FALSE;
+static flag has_stls = FALSE;
#endif /* SSL_ENABLE */
/* mailbox variables initialized in pop3_getrange() */
{
int ok;
+ if (done_capa) {
+ return PS_SUCCESS;
+ }
#if defined(GSSAPI)
has_gssapi = FALSE;
#endif /* defined(GSSAPI) */
break;
#ifdef SSL_ENABLE
if (strstr(buffer, "STLS"))
- has_ssl = TRUE;
+ has_stls = TRUE;
#endif /* SSL_ENABLE */
#if defined(GSSAPI)
if (strstr(buffer, "GSSAPI"))
has_cram = TRUE;
}
}
+ done_capa = TRUE;
return(ok);
}
char *challenge;
#endif /* OPIE_ENABLE */
#ifdef SSL_ENABLE
- flag did_stls = FALSE;
+ flag connection_may_have_tls_errors = FALSE;
+ flag got_tls = FALSE;
#endif /* SSL_ENABLE */
+ done_capa = FALSE;
#if defined(GSSAPI)
has_gssapi = FALSE;
#endif /* defined(GSSAPI) */
has_otp = FALSE;
#endif /* OPIE_ENABLE */
#ifdef SSL_ENABLE
- has_ssl = FALSE;
+ has_stls = FALSE;
#endif /* SSL_ENABLE */
/* Set this up before authentication quits early. */
set_peek_capable(ctl);
+
+ /* Hack: allow user to force RETR. */
+ if (peek_capable && getenv("FETCHMAIL_POP3_FORCE_RETR")) {
+ peek_capable = 0;
+ }
+
/*
* The "Maillennium POP3/PROXY server" deliberately truncates
* TOP replies after c. 64 or 80 kByte (we have varying reports), so
/*
* CAPA command may return a list including available
- * authentication mechanisms. if it doesn't, no harm done, we
- * just fall back to a plain login. Note that this code
- * latches the server's authentication type, so that in daemon mode
- * the CAPA check only needs to be done once at start of run.
+ * authentication mechanisms and STLS capability.
+ *
+ * If it doesn't, no harm done, we just fall back to a plain
+ * login -- if the user allows it.
*
- * If CAPA fails, then force the authentication method to PASSORD
- * and repoll immediately.
+ * Note that this code latches the server's authentication type,
+ * so that in daemon mode the CAPA check only needs to be done
+ * once at start of run.
*
- * These authentication methods are blessed by RFC1734,
- * describing the POP3 AUTHentication command.
+ * If CAPA fails, then force the authentication method to
+ * PASSWORD, switch off opportunistic and repoll immediately.
+ * If TLS is mandatory, fail up front.
*/
if ((ctl->server.authenticate == A_ANY) ||
- (ctl->server.authenticate == A_GSSAPI) ||
- (ctl->server.authenticate == A_KERBEROS_V4) ||
- (ctl->server.authenticate == A_OTP) ||
- (ctl->server.authenticate == A_CRAM_MD5))
+ (ctl->server.authenticate == A_GSSAPI) ||
+ (ctl->server.authenticate == A_KERBEROS_V4) ||
+ (ctl->server.authenticate == A_KERBEROS_V5) ||
+ (ctl->server.authenticate == A_OTP) ||
+ (ctl->server.authenticate == A_CRAM_MD5) ||
+ maybe_tls(ctl))
{
if ((ok = capa_probe(sock)) != PS_SUCCESS)
- /* we are in STAGE_GETAUTH! */
+ /* we are in STAGE_GETAUTH => failure is PS_AUTHFAIL! */
if (ok == PS_AUTHFAIL ||
/* Some servers directly close the socket. However, if we
* have already authenticated before, then a previous CAPA
*/
(ok == PS_SOCKET && !ctl->wehaveauthed))
{
- ctl->server.authenticate = A_PASSWORD;
- /* repoll immediately */
- ok = PS_REPOLL;
- break;
+#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"));
+ return ok;
+ } else if (maybe_tls(ctl)) {
+ /* defeat opportunistic STLS */
+ xfree(ctl->sslproto);
+ ctl->sslproto = xstrdup("");
+ }
+#endif
+ /* If strong authentication was opportunistic, retry without, else fail. */
+ switch (ctl->server.authenticate) {
+ case A_ANY:
+ ctl->server.authenticate = A_PASSWORD;
+ /* FALLTHROUGH */
+ case A_PASSWORD: /* this should only happen with TLS enabled */
+ return PS_REPOLL;
+ default:
+ return PS_AUTHFAIL;
+ }
}
}
#ifdef SSL_ENABLE
- if (has_ssl
- && !ctl->use_ssl
- && (!ctl->sslproto || !strcmp(ctl->sslproto,"tls1")))
- {
- char *realhost;
+ if (maybe_tls(ctl)) {
+ char *commonname;
- realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
- ok = gen_transact(sock, "STLS");
+ commonname = ctl->server.pollname;
+ if (ctl->server.via)
+ commonname = ctl->server.via;
+ if (ctl->sslcommonname)
+ commonname = ctl->sslcommonname;
- /* We use "tls1" instead of ctl->sslproto, as we want STLS,
- * not other SSL protocols
- */
- if (ok == PS_SUCCESS &&
- SSLOpen(sock,ctl->sslcert,ctl->sslkey,"tls1",ctl->sslcertck, ctl->sslcertpath,ctl->sslfingerprint,realhost,ctl->server.pollname) == -1)
+ if (has_stls)
{
- if (!ctl->sslproto && !ctl->wehaveauthed)
+ /* Use "tls1" rather than ctl->sslproto because tls1 is the only
+ * protocol that will work with STARTTLS. Don't need to worry
+ * whether TLS is mandatory or opportunistic unless SSLOpen() fails
+ * (see below). */
+ if (gen_transact(sock, "STLS") == PS_SUCCESS
+ && SSLOpen(sock, ctl->sslcert, ctl->sslkey, "tls1", ctl->sslcertck,
+ ctl->sslcertfile, ctl->sslcertpath, ctl->sslfingerprint, commonname,
+ ctl->server.pollname, &ctl->remotename) != -1)
{
- ctl->sslproto = xstrdup("");
- /* repoll immediately */
- return(PS_REPOLL);
+ /*
+ * RFC 2595 says this:
+ *
+ * "Once TLS has been started, the client MUST discard cached
+ * information about server capabilities and SHOULD re-issue the
+ * CAPABILITY command. This is necessary to protect against
+ * man-in-the-middle attacks which alter the capabilities list prior
+ * to STARTTLS. The server MAY advertise different capabilities
+ * after STARTTLS."
+ *
+ * Now that we're confident in our TLS connection we can
+ * guarantee a secure capability re-probe.
+ */
+ got_tls = TRUE;
+ 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);
+ }
}
- report(stderr,
- GT_("SSL connection failed.\n"));
- return PS_SOCKET;
- }
- did_stls = TRUE;
-
- /*
- * RFC 2595 says this:
- *
- * "Once TLS has been started, the client MUST discard cached
- * information about server capabilities and SHOULD re-issue the
- * CAPABILITY command. This is necessary to protect against
- * man-in-the-middle attacks which alter the capabilities list prior
- * to STARTTLS. The server MAY advertise different capabilities
- * after STARTTLS."
- */
- capa_probe(sock);
- }
+ }
+
+ if (!got_tls) {
+ if (must_tls(ctl)) {
+ /* Config required TLS but we couldn't guarantee it, so we must
+ * stop. */
+ report(stderr, GT_("%s: upgrade to TLS failed.\n"), commonname);
+ return PS_SOCKET;
+ } else {
+ /* We don't know whether the connection is usable, and there's
+ * no command we can reasonably issue to test it (NOOP isn't
+ * allowed til post-authentication), so leave it in an unknown
+ * state, mark it as such, and check more carefully if things
+ * go wrong when we try to authenticate. */
+ connection_may_have_tls_errors = TRUE;
+ if (outlevel >= O_VERBOSE)
+ {
+ report(stdout, GT_("%s: opportunistic upgrade to TLS failed, trying to continue.\n"), commonname);
+ }
+ }
+ }
+ } /* maybe_tls() */
#endif /* SSL_ENABLE */
/*
}
#endif /* OPIE_ENABLE */
- strlcpy(shroud, ctl->password, sizeof(shroud));
- ok = gen_transact(sock, "PASS %s", ctl->password);
- shroud[0] = '\0';
-#ifdef SSL_ENABLE
- /* this is for servers which claim to support TLS, but actually
- * don't! */
- if (did_stls && ok == PS_SOCKET && !ctl->sslproto && !ctl->wehaveauthed)
+ /* KPOP uses out-of-band authentication and does not check what
+ * we send here, so send some random fixed string, to avoid
+ * users switching *to* KPOP accidentally revealing their
+ * password */
+ if ((ctl->server.authenticate == A_ANY
+ || ctl->server.authenticate == A_KERBEROS_V4
+ || ctl->server.authenticate == A_KERBEROS_V5)
+ && (ctl->server.service != NULL
+ && strcmp(ctl->server.service, KPOP_PORT) == 0))
{
- ctl->sslproto = xstrdup("");
- /* repoll immediately */
- ok = PS_REPOLL;
+ ok = gen_transact(sock, "PASS krb_ticket");
+ break;
}
-#endif
+
+ /* check if we are actually allowed to send the password */
+ if (ctl->server.authenticate == A_ANY
+ || ctl->server.authenticate == A_PASSWORD) {
+ strlcpy(shroud, ctl->password, sizeof(shroud));
+ ok = gen_transact(sock, "PASS %s", ctl->password);
+ } else {
+ report(stderr, GT_("We've run out of allowed authenticators and cannot continue.\n"));
+ ok = PS_AUTHFAIL;
+ }
+ memset(shroud, 0x55, sizeof(shroud));
+ shroud[0] = '\0';
break;
case P_APOP:
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 = xmalloc((end-start+1) + strlen(ctl->password) + 1);
+ msg = (char *)xmalloc((end-start+1) + strlen(ctl->password) + 1);
strcpy(msg,start);
strcat(msg,ctl->password);
- strcpy(ctl->digest, MD5Digest((unsigned char *)msg));
+ strcpy((char *)ctl->digest, MD5Digest((unsigned char *)msg));
free(msg);
- ok = gen_transact(sock, "APOP %s %s", ctl->remotename, ctl->digest);
+ ok = gen_transact(sock, "APOP %s %s", ctl->remotename, (char *)ctl->digest);
break;
case P_RPOP:
- if ((ok = gen_transact(sock,"USER %s", ctl->remotename)) == 0)
+ if ((ok = gen_transact(sock,"USER %s", ctl->remotename)) == 0) {
+ strlcpy(shroud, ctl->password, sizeof(shroud));
ok = gen_transact(sock, "RPOP %s", ctl->password);
+ memset(shroud, 0x55, sizeof(shroud));
+ shroud[0] = '\0';
+ }
break;
default:
ok = PS_ERROR;
}
+#ifdef SSL_ENABLE
+ /* this is for servers which claim to support TLS, but actually
+ * don't! */
+ if (connection_may_have_tls_errors
+ && (ok == PS_SOCKET || ok == PS_PROTOCOL))
+ {
+ xfree(ctl->sslproto);
+ ctl->sslproto = xstrdup("");
+ /* repoll immediately without TLS */
+ ok = PS_REPOLL;
+ }
+#endif
+
if (ok != 0)
{
/* maybe we detected a lock-busy condition? */
int got_it;
char buf [POPBUFSIZE+1];
snprintf(buf, sizeof(buf), "TOP %d 1", num);
- if ((ok = gen_transact(sock, buf )) != 0)
+ if ((ok = gen_transact(sock, "%s", buf)) != 0)
return ok;
got_it = 0;
- while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0)
+ while (gen_recv(sock, buf, sizeof(buf)) == 0)
{
if (DOTLINE(buf))
break;
if (mark == UID_DELETED || mark == UID_EXPUNGED)
{
if (outlevel >= O_VERBOSE)
- report(stderr, GT_("id=%s (num=%d) was deleted, but is still present!\n"), id, try_nr);
+ report(stderr, GT_("id=%s (num=%u) was deleted, but is still present!\n"), id, try_nr);
/* just mark it as seen now! */
newl->val.status.mark = mark = UID_SEEN;
}
/* get the total message count */
gen_send(sock, "STAT");
ok = pop3_ok(sock, buf);
- if (ok == 0)
- sscanf(buf,"%d %d", countp, bytes);
- else
+ if (ok == 0) {
+ int asgn;
+
+ asgn = sscanf(buf,"%d %d", countp, bytes);
+ if (asgn != 2)
+ return PS_PROTOCOL;
+ } else
return(ok);
/*
if (dofastuidl)
return(pop3_fastuidl( sock, ctl, *countp, newp));
/* grab the mailbox's UID list */
- if ((ok = gen_transact(sock, "UIDL")) != 0)
+ if (gen_transact(sock, "UIDL") != 0)
{
/* don't worry, yet! do it the slow way */
- if ((ok = pop3_slowuidl(sock, ctl, countp, newp)))
+ if (pop3_slowuidl(sock, ctl, countp, newp))
{
report(stderr, GT_("protocol error while fetching UIDLs\n"));
return(PS_ERROR);
unsigned long unum;
*newp = 0;
- while ((ok = gen_recv(sock, buf, sizeof(buf))) == PS_SUCCESS)
+ while (gen_recv(sock, buf, sizeof(buf)) == PS_SUCCESS)
{
if (DOTLINE(buf))
break;
* the same mail will not be downloaded again.
*/
old = save_str(&ctl->oldsaved, id, UID_UNSEEN);
- old->val.status.num = unum;
}
+ /* save the number */
+ old->val.status.num = unum;
} else
return PS_ERROR;
} /* multi-line loop for UIDL reply */
* as a workaround. */
if (strspn(buf, " \t") == strlen(buf))
strcpy(buf, "<>");
- sdps_envfrom = xmalloc(strlen(buf)+1);
+ sdps_envfrom = (char *)xmalloc(strlen(buf)+1);
strcpy(sdps_envfrom,buf);
break;
case 5:
/* Wrap address with To: <> so nxtaddr() likes it */
- sdps_envto = xmalloc(strlen(buf)+7);
+ sdps_envto = (char *)xmalloc(strlen(buf)+7);
sprintf(sdps_envto,"To: <%s>",buf);
break;
}
static const struct method pop3 =
{
"POP3", /* Post Office Protocol v3 */
- "pop3", /* standard POP3 port */
- "pop3s", /* ssl POP3 port */
+ "pop3", /* port for plain and TLS POP3 */
+ "pop3s", /* port for SSL POP3 */
FALSE, /* this is not a tagged protocol */
TRUE, /* this uses a message delimiter */
pop3_ok, /* parse command response */