]> Pileus Git - ~andy/fetchmail/blobdiff - imap.c
Add CVE number.
[~andy/fetchmail] / imap.c
diff --git a/imap.c b/imap.c
index 5c1d823656f2494e2248cd9884451f9fcd6e8ec3..053b607cf47cb8f81eedf8237a9211d6d212747b 100644 (file)
--- a/imap.c
+++ b/imap.c
@@ -46,7 +46,8 @@ static unsigned int *unseen_messages;
 static int actual_deletions = 0;
 
 /* for "IMAP> IDLE" */
-static int saved_timeout = 0;
+static int saved_timeout = 0, idle_timeout = 0;
+static time_t idle_start_time = 0;
 
 static int imap_ok(int sock, char *argbuf)
 /* parse command response */
@@ -163,6 +164,15 @@ static int imap_ok(int sock, char *argbuf)
                return(PS_LOCKBUSY);
            }
        }
+
+       if (stage == STAGE_IDLE)
+       {
+           /* reduce the timeout: servers may not reset their timeout
+            * when they send some information asynchronously */
+           mytimeout = idle_timeout - (time((time_t *) NULL) - idle_start_time);
+           if (mytimeout <= 0)
+               return(PS_IDLETIMEOUT);
+       }
     } while
        (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
 
@@ -370,11 +380,11 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
 {
     int ok = 0;
 #ifdef SSL_ENABLE
-    flag did_stls = FALSE;
-    flag using_tls = FALSE;
-#endif /* SSL_ENABLE */
-
+    int got_tls = 0;
+    char *realhost;
+#endif
     (void)greeting;
+
     /*
      * Assumption: expunges are cheap, so we want to do them
      * after every message unless user said otherwise.
@@ -397,55 +407,63 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
     }
 
 #ifdef SSL_ENABLE
-    if ((!ctl->sslproto || !strcasecmp(ctl->sslproto,"tls1"))
-        && !ctl->use_ssl
-        && strstr(capabilities, "STARTTLS"))
-    {
-           char *realhost;
-
-           realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
-           ok = gen_transact(sock, "STARTTLS");
-
-           /* We use "tls1" instead of ctl->sslproto, as we want STARTTLS,
-            * 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,&ctl->remotename) == -1)
-           {
-              if (!ctl->sslproto && !ctl->wehaveauthed)
-              {
-                  ctl->sslproto = xstrdup("");
-                  /* repoll immediately with TLS disabled */
-                  return(PS_REPOLL);
-              }
-               report(stderr,
-                      GT_("TLS connection failed.\n"));
-               return PS_SOCKET;
-           } else {
-              using_tls = TRUE;
-              if (outlevel >= O_VERBOSE && !ctl->sslproto)
-                  report(stdout, GT_("%s: opportunistic upgrade to TLS.\n"), realhost);
-          }
-          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, ctl);
-    }
-    /* Check if TLS was enforced. */
-    if ((ctl->sslproto && !strcasecmp(ctl->sslproto,"tls1")) && !ctl->use_ssl && !using_tls) {
-       report(stderr, GT_("TLS connection failed.\n"));
-       return PS_SOCKET;
+    realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
+
+    if (maybe_tls(ctl)) {
+       if (strstr(capabilities, "STARTTLS"))
+       {
+           /* 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, "STARTTLS") == PS_SUCCESS
+                   && SSLOpen(sock, ctl->sslcert, ctl->sslkey, "tls1", ctl->sslcertck,
+                       ctl->sslcertpath, ctl->sslfingerprint, realhost,
+                       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.
+                */
+               got_tls = 1;
+               capa_probe(sock, ctl);
+               if (outlevel >= O_VERBOSE)
+               {
+                   report(stdout, GT_("%s: upgrade to TLS succeeded.\n"), realhost);
+               }
+           }
+       }
+
+       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"), realhost);
+               return PS_SOCKET;
+           } else {
+               if (outlevel >= O_VERBOSE) {
+                   report(stdout, GT_("%s: opportunistic upgrade to TLS failed, trying to continue\n"), realhost);
+               }
+               /* We don't know whether the connection is in a working state, so
+                * test by issuing a NOOP. */
+               if (gen_transact(sock, "NOOP") != PS_SUCCESS) {
+                   /* Not usable.  Empty sslproto to force an unencrypted
+                    * connection on the next attempt, and repoll. */
+                   ctl->sslproto = xstrdup("");
+                   return PS_REPOLL;
+               }
+               /* Usable.  Proceed with authenticating insecurely. */
+           }
+       }
     }
 #endif /* SSL_ENABLE */
 
@@ -602,19 +620,11 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
 
        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);
-#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)
-       {
-           ctl->sslproto = xstrdup("");
-           /* repoll immediately */
-           ok = PS_REPOLL;
-       }
-#endif
        if (ok)
        {
            /* SASL cancellation of authentication */
@@ -676,7 +686,8 @@ static int imap_idle(int sock)
        /* special timeout to terminate the IDLE and re-issue it
         * at least every 28 minutes:
         * (the server may have an inactivity timeout) */
-       mytimeout = 1680; /* 28 min */
+       mytimeout = idle_timeout = 1680; /* 28 min */
+       time(&idle_start_time);
        stage = STAGE_IDLE;
        /* enter IDLE mode */
        ok = gen_transact(sock, "IDLE");
@@ -704,7 +715,8 @@ static int imap_idle(int sock)
             * notification out of the blue. This is in compliance
             * with RFC 2060 section 5.3. Wait for that with a low
             * timeout */
-           mytimeout = 28;
+           mytimeout = idle_timeout = 28;
+           time(&idle_start_time);
            stage = STAGE_IDLE;
            /* We are waiting for notification; no tag needed */
            tag[0] = '\0';