]> Pileus Git - ~andy/fetchmail/blobdiff - pop3.c
Credit John Beck's fixes.
[~andy/fetchmail] / pop3.c
diff --git a/pop3.c b/pop3.c
index b41d4e5396265521519f70385e7b836879437b1c..5549dc9ac5a56ff41e3efbbb7bbb6a61e581cd99 100644 (file)
--- a/pop3.c
+++ b/pop3.c
@@ -41,6 +41,7 @@ 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) */
@@ -51,6 +52,9 @@ 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_stls = FALSE;
 #endif /* SSL_ENABLE */
@@ -81,60 +85,17 @@ char *sdps_envto;
 static int do_pop3_ntlm(int sock, struct query *ctl,
        int msn_instead /** if true, send AUTH MSN, else send AUTH NTLM */)
 {
-    tSmbNtlmAuthRequest request;
-    tSmbNtlmAuthChallenge challenge;
-    tSmbNtlmAuthResponse response;
+    char msgbuf[POPBUFSIZE+1];
+    int result;
 
-    char msgbuf[2048];
-    int result,len;
-  
     gen_send(sock, msn_instead ? "AUTH MSN" : "AUTH NTLM");
 
-    if ((result = gen_recv(sock, msgbuf, sizeof msgbuf)))
-       return result;
-  
-    if (msgbuf[0] != '+')
-       return PS_AUTHFAIL;
-  
-    buildSmbNtlmAuthRequest(&request,ctl->remotename,NULL);
-
-    if (outlevel >= O_DEBUG)
-       dumpSmbNtlmAuthRequest(stdout, &request);
-
-    memset(msgbuf,0,sizeof msgbuf);
-    to64frombits (msgbuf, &request, SmbLength(&request));
-  
-    if (outlevel >= O_MONITOR)
-       report(stdout, "POP3> %s\n", msgbuf);
-  
-    strcat(msgbuf,"\r\n");
-    SockWrite (sock, msgbuf, strlen (msgbuf));
-
-    if ((gen_recv(sock, msgbuf, sizeof msgbuf)))
+    if ((result = ntlm_helper(sock, ctl, "POP3")))
        return result;
-  
-    len = from64tobits (&challenge, msgbuf, sizeof(msgbuf));
-    
-    if (outlevel >= O_DEBUG)
-       dumpSmbNtlmAuthChallenge(stdout, &challenge);
-    
-    buildSmbNtlmAuthResponse(&challenge, &response,ctl->remotename,ctl->password);
-  
-    if (outlevel >= O_DEBUG)
-       dumpSmbNtlmAuthResponse(stdout, &response);
-  
-    memset(msgbuf,0,sizeof msgbuf);
-    to64frombits (msgbuf, &response, SmbLength(&response));
-
-    if (outlevel >= O_MONITOR)
-       report(stdout, "POP3> %s\n", msgbuf);
-      
-    strcat(msgbuf,"\r\n");
-    SockWrite (sock, msgbuf, strlen (msgbuf));
-  
+
     if ((result = gen_recv (sock, msgbuf, sizeof msgbuf)))
        return result;
-  
+
     if (strstr (msgbuf, "OK"))
        return PS_SUCCESS;
     else
@@ -238,6 +199,9 @@ static int capa_probe(int sock)
 {
     int        ok;
 
+    if (done_capa) {
+       return PS_SUCCESS;
+    }
 #if defined(GSSAPI)
     has_gssapi = FALSE;
 #endif /* defined(GSSAPI) */
@@ -248,6 +212,9 @@ static int capa_probe(int sock)
 #ifdef OPIE_ENABLE
     has_otp = FALSE;
 #endif /* OPIE_ENABLE */
+#ifdef NTLM_ENABLE
+    has_ntlm = FALSE;
+#endif /* NTLM_ENABLE */
 
     ok = gen_transact(sock, "CAPA");
     if (ok == PS_SUCCESS)
@@ -259,26 +226,37 @@ static int capa_probe(int sock)
        {
            if (DOTLINE(buffer))
                break;
+
 #ifdef SSL_ENABLE
            if (strstr(buffer, "STLS"))
                has_stls = TRUE;
 #endif /* SSL_ENABLE */
+
 #if defined(GSSAPI)
            if (strstr(buffer, "GSSAPI"))
                has_gssapi = TRUE;
 #endif /* defined(GSSAPI) */
+
 #if defined(KERBEROS_V4)
            if (strstr(buffer, "KERBEROS_V4"))
                has_kerberos = TRUE;
 #endif /* defined(KERBEROS_V4)  */
+
 #ifdef OPIE_ENABLE
            if (strstr(buffer, "X-OTP"))
                has_otp = TRUE;
 #endif /* OPIE_ENABLE */
+
+#ifdef NTLM_ENABLE
+           if (strstr(buffer, "NTLM"))
+               has_ntlm = TRUE;
+#endif /* NTLM_ENABLE */
+
            if (strstr(buffer, "CRAM-MD5"))
                has_cram = TRUE;
        }
     }
+    done_capa = TRUE;
     return(ok);
 }
 
@@ -302,11 +280,10 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
     char *challenge;
 #endif /* OPIE_ENABLE */
 #ifdef SSL_ENABLE
-    char *realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
     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) */
@@ -323,6 +300,12 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
 
     /* 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
@@ -339,9 +322,9 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
      *
      * Matthias Andree
      */
-    if (peek_capable && strstr(greeting, "Maillennium POP3/PROXY server")) {
+    if (peek_capable && strstr(greeting, "Maillennium POP3")) {
        if ((ctl->server.workarounds & WKA_TOP) == 0) {
-           report(stdout, GT_("Warning: \"Maillennium POP3/PROXY server\" found, using RETR command instead of TOP.\n"));
+           report(stdout, GT_("Warning: \"Maillennium POP3\" found, using RETR command instead of TOP.\n"));
            ctl->server.workarounds |= WKA_TOP;
        }
        peek_capable = 0;
@@ -360,22 +343,7 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
         ctl->server.sdps = TRUE;
 #endif /* SDPS_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)
-       return (do_pop3_ntlm(sock, ctl, 0) == 0) ? PS_SUCCESS : PS_AUTHFAIL;
-#else
-    if (ctl->server.authenticate == A_NTLM || ctl->server.authenticate == A_MSN)
-    {
-       report(stderr,
-          GT_("Required NTLM capability not compiled into fetchmail\n"));
-    }
-#endif
-
-    switch (ctl->server.protocol) {
+   switch (ctl->server.protocol) {
     case P_POP3:
 #ifdef RPA_ENABLE
        /* XXX FIXME: AUTH probing (RFC1734) should become global */
@@ -406,25 +374,29 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
 
        /*
         * 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 CAPA fails, then force the authentication method to PASSORD
-        * and repoll immediately.
+        * If it doesn't, no harm done, we just fall back to a plain
+        * login -- if the user allows it.
         *
-        * These authentication methods are blessed by RFC1734,
-        * describing the POP3 AUTHentication command.
+        * 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_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
@@ -433,25 +405,52 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
                     */
                    (ok == PS_SOCKET && !ctl->wehaveauthed))
                {
-                   ctl->server.authenticate = A_PASSWORD;
-                   /* repoll immediately with PASS authentication */
-                   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 (maybe_tls(ctl)) {
-          if (has_stls)
+           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
-                      && SSLOpen(sock, ctl->sslcert, ctl->sslkey, "tls1", ctl->sslcertck,
-                          ctl->sslcertpath, ctl->sslfingerprint, realhost,
-                          ctl->server.pollname, &ctl->remotename) != -1)
+                      && (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:
@@ -466,20 +465,21 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
                    * Now that we're confident in our TLS connection we can
                    * guarantee a secure capability re-probe.
                    */
-                  got_tls = TRUE;
-                  (void)capa_probe(sock);
+                  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"), realhost);
+                      report(stdout, GT_("%s: upgrade to TLS succeeded.\n"), commonname);
                   }
-              }
-          }
-
-          if (!got_tls) {
-              if (must_tls(ctl)) {
+              } else 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);
+                  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
@@ -487,14 +487,15 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
                    * 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"), realhost);
+                      report(stdout, GT_("%s: opportunistic upgrade to TLS failed, trying to continue.\n"), commonname);
                   }
               }
           }
-       }
+       } /* maybe_tls() */
 #endif /* SSL_ENABLE */
 
        /*
@@ -520,7 +521,8 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
 #if defined(GSSAPI)
        if (has_gssapi &&
            (ctl->server.authenticate == A_GSSAPI ||
-            ctl->server.authenticate == A_ANY))
+           (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)
@@ -539,7 +541,25 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
        }
 #endif /* OPIE_ENABLE */
 
-       if (ctl->server.authenticate == A_CRAM_MD5 || 
+#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);
@@ -578,19 +598,31 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
        }
 #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 (connection_may_have_tls_errors && ok == PS_SOCKET)
+       /* 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 without TLS */
-           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:
@@ -615,19 +647,37 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
        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:
@@ -635,6 +685,19 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting)
        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? */
@@ -683,10 +746,10 @@ static int pop3_gettopid(int sock, int num , char *id, size_t idsize)
     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;
@@ -752,6 +815,7 @@ static int pop3_fastuidl( int sock,  struct query *ctl, unsigned int count, int
     int ok;
     unsigned int first_nr, last_nr, try_nr;
     char id [IDLEN+1];
+    struct idlist *savep = NULL; /** pointer to cache save_str result, speeds up saves */
 
     first_nr = 0;
     last_nr = count + 1;
@@ -768,7 +832,7 @@ static int pop3_fastuidl( int sock,  struct query *ctl, unsigned int count, int
            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;
            }
@@ -793,8 +857,8 @@ static int pop3_fastuidl( int sock,  struct query *ctl, unsigned int count, int
            last_nr = try_nr;
 
            /* save it */
-           newl = save_str(&ctl->oldsaved, id, UID_UNSEEN);
-           newl->val.status.num = try_nr;
+           savep = save_str(savep ? &savep : &ctl->oldsaved, id, UID_UNSEEN);
+           savep->val.status.num = try_nr;
        }
     }
     if (outlevel >= O_DEBUG && last_nr <= count)
@@ -921,9 +985,13 @@ static int pop3_getrange(int sock,
     /* 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);
 
     /*
@@ -977,10 +1045,10 @@ static int pop3_getrange(int sock,
            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);
@@ -990,18 +1058,19 @@ static int pop3_getrange(int sock,
            {
                /* UIDL worked - parse reply */
                unsigned long unum;
+               struct idlist *newl = NULL;
 
                *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;
 
                    if (parseuid(buf, &unum, id, sizeof(id)) == PS_SUCCESS)
                    {
-                       struct idlist   *old, *newl;
+                       struct idlist   *old;
 
-                       newl = save_str(&ctl->newsaved, id, UID_UNSEEN);
+                       newl = save_str(newl ? &newl : &ctl->newsaved, id, UID_UNSEEN);
                        newl->val.status.num = unum;
 
                        if ((old = str_in_list(&ctl->oldsaved, id, FALSE)))
@@ -1201,12 +1270,12 @@ static int pop3_fetch(int sock, struct query *ctl, int number, int *lenp)
                 * 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;
             }