Ejemplo n.º 1
0
/* XXX FIXME: using the Message-ID is unsafe, some messages (spam,
 * broken messages) do not have Message-ID headers, and messages without
 * those appear to break this code and cause fetchmail (at least version
 * 6.2.3) to not delete such messages properly after retrieval.
 * See Sourceforge Bug #780933.
 *
 * The other problem is that the TOP command itself is optional, too... */
static int pop3_gettopid(int sock, int num , char *id, size_t idsize)
{
    int ok;
    int got_it;
    char buf [POPBUFSIZE+1];
    snprintf(buf, sizeof(buf), "TOP %d 1", num);
    if ((ok = gen_transact(sock, "%s", buf)) != 0)
       return ok;
    got_it = 0;
    while (gen_recv(sock, buf, sizeof(buf)) == 0)
    {
	if (DOTLINE(buf))
	    break;
	if (!got_it && 0 == strncasecmp("Message-Id:", buf, 11)) {
	    char *p = buf + 11;
	    got_it = 1;
	    p += strspn(p, POSIX_space);
	    strlcpy(id, p, idsize);
	    trim(id);
	}
    }
    /* XXX FIXME: do not return success here if no Message-ID header was
     * found. */
    return 0;
}
Ejemplo n.º 2
0
static int odmr_logout(int sock, struct query *ctl)
/* send logout command */
{
    /* if we have a smtp_socket, then we've turned around and the
       local smtp server is in control of the connection (so we don't
       send QUIT) */
    if (ctl->smtp_socket == -1)
       return(gen_transact(sock, "QUIT"));
    else
       return(PS_SUCCESS);
}
Ejemplo n.º 3
0
static int pop3_getauth(int sock, struct query *ctl, char *greeting)
/* apply for connection authorization */
{
    int ok;
    char *start,*end;
    char *msg;
#ifdef OPIE_ENABLE
    char *challenge;
#endif /* OPIE_ENABLE */
#ifdef SSL_ENABLE
    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) */
#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);

    /* 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
     * disable TOP. Comcast once spewed marketing babble to the extent
     * of protecting Outlook -- pretty overzealous to break a protocol
     * for that that Microsoft could have read, too. Comcast aren't
     * alone in using this software though.
     * <http://lists.ccil.org/pipermail/fetchmail-friends/2004-April/008523.html>
     * (Thanks to Ed Wilts for reminding me of that.)
     *
     * The warning is printed once per server, until fetchmail exits.
     * It will be suppressed when --fetchall or other circumstances make
     * us use RETR anyhow.
     *
     * Matthias Andree
     */
    if (peek_capable && strstr(greeting, "Maillennium POP3/PROXY server")) {
	if ((ctl->server.workarounds & WKA_TOP) == 0) {
	    report(stdout, GT_("Warning: \"Maillennium POP3/PROXY server\" found, using RETR command instead of TOP.\n"));
	    ctl->server.workarounds |= WKA_TOP;
	}
	peek_capable = 0;
    }
    if (ctl->server.authenticate == A_SSH) {
        return PS_SUCCESS;
    }

#ifdef SDPS_ENABLE
    /*
     * This needs to catch both demon.co.uk and demon.net.
     * If we see either, and we're in multidrop mode, try to use
     * the SDPS *ENV extension.
     */
    if (!(ctl->server.sdps) && MULTIDROP(ctl) && strstr(greeting, "demon."))
        ctl->server.sdps = TRUE;
#endif /* SDPS_ENABLE */

   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.
	 *
	 * 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_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 => 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)
	   {
	       /* 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)
	       {
		   /*
		    * 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);
		   }
	       }
	   }

	   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 */

	/*
	 * OK, we have an authentication type now.
	 */
#if defined(KERBEROS_V4)
	/* 
	 * Servers doing KPOP have to go through a dummy login sequence
	 * rather than doing SASL.
	 */
	if (has_kerberos &&
	    ctl->server.service && (strcmp(ctl->server.service, KPOP_PORT)!=0)
	    && (ctl->server.authenticate == A_KERBEROS_V4
	     || ctl->server.authenticate == A_KERBEROS_V5
	     || ctl->server.authenticate == A_ANY))
	{
	    ok = do_rfc1731(sock, "AUTH", ctl->server.truename);
	    if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
		break;
	}
#endif /* defined(KERBEROS_V4) || defined(KERBEROS_V5) */

#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;
	}

	/* ordinary validation, no one-time password or RPA */ 
	if ((ok = gen_transact(sock, "USER %s", ctl->remotename)))
	    break;

#ifdef OPIE_ENABLE
	/* see RFC1938: A One-Time Password System */
	if ((challenge = strstr(lastok, "otp-"))) {
	  char response[OPIE_RESPONSE_MAX+1];
	  int i;
	  char *n = xstrdup("");

	  i = opiegenerator(challenge, !strcmp(ctl->password, "opie") ? n : ctl->password, response);
	  free(n);
	  if ((i == -2) && !run.poll_interval) {
	    char secret[OPIE_SECRET_MAX+1];
	    fprintf(stderr, GT_("Secret pass phrase: "));
	    if (opiereadpass(secret, sizeof(secret), 0))
	      i = opiegenerator(challenge,  secret, response);
	    memset(secret, 0, sizeof(secret));
	  };

	  if (i) {
	    ok = PS_ERROR;
	    break;
	  };

	  ok = gen_transact(sock, "PASS %s", response);
	  break;
	}
#endif /* OPIE_ENABLE */

	/* 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))
	{
	    ok = gen_transact(sock, "PASS krb_ticket");
	    break;
	}

	/* 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:
	/* build MD5 digest from greeting timestamp + password */
	/* find start of timestamp */
	for (start = greeting;  *start != 0 && *start != '<';  start++)
	    continue;
	if (*start == 0) {
	    report(stderr,
		   GT_("Required APOP timestamp not found in greeting\n"));
	    return(PS_AUTHFAIL);
	}

	/* find end of timestamp */
	for (end = start;  *end != 0  && *end != '>';  end++)
	    continue;
	if (*end == 0 || end == start + 1) {
	    report(stderr, 
		   GT_("Timestamp syntax error in greeting\n"));
	    return(PS_AUTHFAIL);
	}
	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 = (char *)xmalloc((end-start+1) + strlen(ctl->password) + 1);
	strcpy(msg,start);
	strcat(msg,ctl->password);
	strcpy((char *)ctl->digest, MD5Digest((unsigned char *)msg));
	free(msg);

	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) {
	    strlcpy(shroud, ctl->password, sizeof(shroud));
	    ok = gen_transact(sock, "RPOP %s", ctl->password);
	    memset(shroud, 0x55, sizeof(shroud));
	    shroud[0] = '\0';
	}
	break;

    default:
	report(stderr, GT_("Undefined protocol request in POP3_auth\n"));
	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? */
        if (ok == PS_LOCKBUSY)
	    report(stderr, GT_("lock busy!  Is another session active?\n")); 

	return(ok);
    }

/* Disable the sleep. Based on patch by Brian Candler 2004-04-19/2004-11-08,
 * accepted by Matthias Andree.
 *
 * Rationale: the server must have locked the spool before returning +OK;
 * this sleep just wastes time and hence, for modem and GSM CSD users, money. */
#ifdef WANT_BOGUS
    /*
     * Empirical experience shows some server/OS combinations
     * may need a brief pause even after any lockfiles on the
     * server are released, to give the server time to finish
     * copying back very large mailfolders from the temp-file...
     * this is only ever an issue with extremely large mailboxes.
     */
    sleep(3); /* to be _really_ safe, probably need sleep(5)! */
#endif

    /* we're approved */
    return(PS_SUCCESS);
}
Ejemplo n.º 4
0
static int capa_probe(int sock)
/* probe the capabilities of the remote server */
{
    int	ok;

    if (done_capa) {
	return PS_SUCCESS;
    }
#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 NTLM_ENABLE
    has_ntlm = FALSE;
#endif /* NTLM_ENABLE */

    ok = gen_transact(sock, "CAPA");
    if (ok == PS_SUCCESS)
    {
	char buffer[64];

	/* determine what authentication methods we have available */
	while ((ok = gen_recv(sock, buffer, sizeof(buffer))) == 0)
	{
	    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);
}