+ /*
+ * CAPA command may return a list including available
+ * 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.
+ *
+ * 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.
+ *
+ * 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_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 => 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
+ * must have succeeded. In that case, treat this as a
+ * genuine socket error and do not change the auth method.
+ */
+ (ok == PS_SOCKET && !ctl->wehaveauthed))
+ {
+#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 (maybe_tls(ctl)) {
+ char *commonname;
+
+ commonname = ctl->server.pollname;
+ if (ctl->server.via)
+ commonname = ctl->server.via;
+ if (ctl->sslcommonname)
+ commonname = ctl->sslcommonname;
+
+ if (has_stls
+ || must_tls(ctl)) /* if TLS is mandatory, ignore capabilities */
+ {
+ /* 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
+ && (set_timeout(mytimeout), SSLOpen(sock, ctl->sslcert, ctl->sslkey, "tls1", ctl->sslcertck,
+ ctl->sslcertfile, ctl->sslcertpath, ctl->sslfingerprint, commonname,
+ ctl->server.pollname, &ctl->remotename)) != -1)
+ {
+ /*
+ * 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.
+ */
+ 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);
+ }
+ } else if (must_tls(ctl)) {
+ /* Config required TLS but we couldn't guarantee it, so we must
+ * stop. */
+ set_timeout(0);
+ 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. */
+ set_timeout(0);
+ 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 */
+
+ /*
+ * OK, we have an authentication type now.
+ */
+
+#if defined(GSSAPI)
+ if (has_gssapi &&
+ (ctl->server.authenticate == A_GSSAPI ||
+ (ctl->server.authenticate == A_ANY
+ && check_gss_creds("pop", ctl->server.truename) == PS_SUCCESS)))
+ {
+ ok = do_gssauth(sock,"AUTH","pop",ctl->server.truename,ctl->remotename);
+ if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
+ break;
+ }
+#endif /* defined(GSSAPI) */
+
+#ifdef OPIE_ENABLE
+ if (has_otp &&
+ (ctl->server.authenticate == A_OTP ||
+ ctl->server.authenticate == A_ANY))
+ {
+ ok = do_otp(sock, "AUTH", ctl);
+ if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
+ break;
+ }
+#endif /* OPIE_ENABLE */
+
+#ifdef NTLM_ENABLE
+ /* MSN servers require the use of NTLM (MSN) authentication */
+ if (!strcasecmp(ctl->server.pollname, "pop3.email.msn.com") ||
+ ctl->server.authenticate == A_MSN)
+ return (do_pop3_ntlm(sock, ctl, 1) == 0) ? PS_SUCCESS : PS_AUTHFAIL;
+ if (ctl->server.authenticate == A_NTLM || (has_ntlm && ctl->server.authenticate == A_ANY)) {
+ ok = do_pop3_ntlm(sock, ctl, 0);
+ if (ok == 0 || ctl->server.authenticate != A_ANY)
+ break;
+ }
+#else
+ if (ctl->server.authenticate == A_NTLM || ctl->server.authenticate == A_MSN)
+ {
+ report(stderr,
+ GT_("Required NTLM capability not compiled into fetchmail\n"));
+ }
+#endif
+
+ if (ctl->server.authenticate == A_CRAM_MD5 ||
+ (has_cram && ctl->server.authenticate == A_ANY))
+ {
+ ok = do_cram_md5(sock, "AUTH", ctl, NULL);
+ if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
+ break;
+ }
+
+ if (ctl->server.authenticate == A_APOP
+ || ctl->server.authenticate == A_ANY)
+ {
+ ok = do_apop(sock, ctl, greeting);
+ if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
+ break;
+ }
+
+ /* ordinary validation, no one-time password or RPA */
+ if ((ok = gen_transact(sock, "USER %s", ctl->remotename)))
+ break;
+
+#ifdef OPIE_ENABLE