Ejemplo n.º 1
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.º 2
0
static int odmr_getrange(int sock, struct query *ctl, const char *id, 
			 int *countp, int *newp, int *bytes)
/* send ODMR and then run a reverse SMTP session */
{
    int ok, opts, smtp_sock;
    int doing_smtp_data = 0;   /* Are we in SMTP DATA state? */
    char buf [MSGBUFSIZE+1];
    struct idlist *qnp;		/* pointer to Q names */

    (void)id;
    if ((ok = SMTP_ehlo(sock, SMTP_MODE, fetchmailhost, 
			ctl->server.esmtp_name, ctl->server.esmtp_password,
			&opts)))
    {
	report(stderr, GT_("%s's SMTP listener does not support ESMTP\n"),
	      ctl->server.pollname);
	return(ok);
    }
    else if (!(opts & ESMTP_ATRN))
    {
	report(stderr, GT_("%s's SMTP listener does not support ATRN\n"),
	      ctl->server.pollname);
	return(PS_PROTOCOL);
    }

    /* make sure we don't enter the fetch loop */
    *bytes = *countp = *newp = -1;

    /* authenticate via CRAM-MD5 */
    ok = do_cram_md5(sock, "AUTH", ctl, "334 ");
    if (ok)
	return(ok);

    /*
     * By default, the hostlist has a single entry, the fetchmail host's
     * canonical DNS name.
     */
    buf[0] = '\0';
    for (qnp = ctl->domainlist; qnp; qnp = qnp->next)
	if (strlen(buf) + strlen(qnp->id) + 1 >= sizeof(buf))
	    break;
	else
	{
	    strcat(buf, qnp->id);
	    strcat(buf, ",");
	}
    buf[strlen(buf) - 1] = '\0';	/* nuke final comma */

    /* ship the domain list and get turnaround */
    gen_send(sock, "ATRN %s", buf);
    if ((ok = gen_recv(sock, buf, sizeof(buf))))
	return(ok);

    /* this switch includes all response codes described in RFC2645 */
    switch(atoi(buf))
    {
    case 250:	/* OK, turnaround is about to happe */
	if (outlevel > O_SILENT)
	    report(stdout, GT_("Turnaround now...\n"));
	break;

    case 450:	/* ATRN request refused */
	if (outlevel > O_SILENT)
	    report(stdout, GT_("ATRN request refused.\n"));
	return(PS_PROTOCOL);

    case 451:	/* Unable to process ATRN request now */
	report(stderr, GT_("Unable to process ATRN request now\n"));
	return(PS_EXCLUDE);

    case 453:	/* You have no mail */
	if (outlevel > O_SILENT)
	    report(stderr, GT_("You have no mail.\n"));
	return(PS_NOMAIL);

    case 502:	/* Command not implemented */
	report(stderr, GT_("Command not implemented\n"));
	return(PS_PROTOCOL);

    case 530:	/* Authentication required */
	report(stderr, GT_("Authentication required.\n"));
	return(PS_AUTHFAIL);

    default: {
	    char *t = sdump(buf, strlen(buf));
	    report(stderr, GT_("Unknown ODMR error \"%s\"\n"), t);
	    xfree(t);
	    return(PS_PROTOCOL);
	}
    }

    /*
     * OK, if we got here it's time to become a pipe between the ODMR
     * remote server (sending) and the SMTP listener we've designated
     * (receiving).  We're not going to try to be a protocol machine;
     * instead, we'll use select(2) to watch the read sides of both
     * sockets and just throw their data at each other.
     */
    if ((smtp_sock = smtp_setup(ctl)) == -1)
	return(PS_SOCKET);
    else
    {
	int	maxfd = (sock > smtp_sock) ? sock : smtp_sock;

	for (;;)
	{
	    fd_set	readfds;
	    struct timeval timeout;

	    FD_ZERO(&readfds);
	    FD_SET(sock, &readfds);
	    FD_SET(smtp_sock, &readfds);

	    timeout.tv_sec  = ctl->server.timeout;
	    timeout.tv_usec = 0;

	    if (select(maxfd+1, &readfds, NULL, NULL, &timeout) == -1)
		return(PS_PROTOCOL);		/* timeout */

	    if (FD_ISSET(sock, &readfds))
	    {
		int n = SockRead(sock, buf, sizeof(buf));
		if (n <= 0)
		    break;

		SockWrite(smtp_sock, buf, n);
		if (outlevel >= O_MONITOR && !doing_smtp_data)
		    report(stdout, "ODMR< %s", buf);
	    }
	    if (FD_ISSET(smtp_sock, &readfds))
	    {
		int n = SockRead(smtp_sock, buf, sizeof(buf));
		if (n <= 0)
		    break;

		SockWrite(sock, buf, n);
		if (outlevel >= O_MONITOR)
		    report(stdout, "ODMR> %s", buf);

               /* We are about to receive message data if the local MTA
                * sends 354 (after receiving DATA) */
               if (!doing_smtp_data && !strncmp(buf, "354", 3))
               {
                   doing_smtp_data = 1;
		   if (outlevel > O_SILENT)
		       report(stdout, GT_("receiving message data\n"));
               }
               else if (doing_smtp_data)
                   doing_smtp_data = 0;
	    }
	}
	SockClose(smtp_sock);
    }

    return(0);
}