From c09c621af7ad0bb81a5ab96ed0e0ce4e99ae3119 Mon Sep 17 00:00:00 2001 From: Steve Holme Date: Sat, 9 Jun 2012 13:49:37 +0100 Subject: [PATCH] pop3: Added support for apop authentication --- RELEASE-NOTES | 1 + lib/pop3.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++--- lib/pop3.h | 2 ++ 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index fa85b190b..80bed0457 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -16,6 +16,7 @@ This release includes the following changes: o pop3: Added support for sasl ntlm authentication o pop3: Added support for sasl cram-md5 authentication o pop3: Added support for sasl digest-md5 authentication + o pop3: Added support for apop authentication This release includes the following bugfixes: diff --git a/lib/pop3.c b/lib/pop3.c index 1e399a03b..8ce7f259b 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -84,6 +84,8 @@ #include "url.h" #include "rawstr.h" #include "curl_sasl.h" +#include "curl_md5.h" +#include "warnless.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -213,8 +215,9 @@ static const struct Curl_handler Curl_handler_pop3s_proxy = { #endif /* Function that checks for an ending pop3 status code at the start of the - given string, but also detects the supported authentication types as well - as the allowed SASL authentication mechanisms within the CAPA response. */ + given string, but also detects the APOP timestamp from the server greeting + as well as the supported authentication types and allowed SASL mechanisms + from the CAPA response. */ static int pop3_endofresp(struct pingpong *pp, int *resp) { char *line = pp->linestart_resp; @@ -222,6 +225,7 @@ static int pop3_endofresp(struct pingpong *pp, int *resp) struct connectdata *conn = pp->conn; struct pop3_conn *pop3c = &conn->proto.pop3c; size_t wordlen; + size_t i; /* Do we have an error response? */ if(len >= 4 && !memcmp("-ERR", line, 4)) { @@ -230,8 +234,31 @@ static int pop3_endofresp(struct pingpong *pp, int *resp) return FALSE; } - /* Are we processing reponses to our CAPA command? */ - if(pop3c->state == POP3_CAPA) { + /* Are we processing servergreet responses */ + if(pop3c->state == POP3_SERVERGREET) { + /* Look for the APOP timestamp */ + if(len >= 3 && line[len - 3] == '>') { + for(i = 0; i < len - 3; ++i) { + if(line[i] == '<') { + /* Calculate the length of the timestamp */ + size_t timestamplen = len - 2 - i; + + /* Allocate some memory for the timestamp */ + pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1); + + if(!pop3c->apoptimestamp) + break; + + /* Copy the timestamp */ + memcpy(pop3c->apoptimestamp, line + i, timestamplen); + pop3c->apoptimestamp[timestamplen] = '\0'; + break; + } + } + } + } + /* Are we processing CAPA command responses? */ + else if(pop3c->state == POP3_CAPA) { /* Do we have the terminating character? */ if(len >= 1 && !memcmp(line, ".", 1)) { @@ -334,6 +361,7 @@ static void state(struct connectdata *conn, pop3state newstate) "AUTH_NTLM", "AUTH_NTLM_TYPE2MSG", "AUTH", + "APOP", "USER", "PASS", "COMMAND", @@ -393,6 +421,40 @@ static CURLcode pop3_state_user(struct connectdata *conn) return CURLE_OK; } +static CURLcode pop3_state_apop(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &conn->proto.pop3c; + size_t i; + MD5_context *ctxt; + unsigned char digest[MD5_DIGEST_LEN]; + char secret[2 * MD5_DIGEST_LEN + 1]; + + ctxt = Curl_MD5_init(Curl_DIGEST_MD5); + if(!ctxt) + return CURLE_OUT_OF_MEMORY; + + Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp, + curlx_uztoui(strlen(pop3c->apoptimestamp))); + + Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd, + curlx_uztoui(strlen(conn->passwd))); + + /* Finalise the digest */ + Curl_MD5_final(ctxt, digest); + + /* Convert the calculated 16 octet digest into a 32 byte hex string */ + for(i = 0; i < MD5_DIGEST_LEN; i++) + snprintf(&secret[2 * i], 3, "%02x", digest[i]); + + result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret); + + if(!result) + state(conn, POP3_APOP); + + return result; +} + static CURLcode pop3_authenticate(struct connectdata *conn) { CURLcode result = CURLE_OK; @@ -542,6 +604,8 @@ static CURLcode pop3_state_capa_resp(struct connectdata *conn, /* Check supported authentication types by decreasing order of security */ if(conn->proto.pop3c.authtypes & POP3_TYPE_SASL) result = pop3_authenticate(conn); + else if(conn->proto.pop3c.authtypes & POP3_TYPE_APOP) + result = pop3_state_apop(conn); else if(conn->proto.pop3c.authtypes & POP3_TYPE_CLEARTEXT) result = pop3_state_user(conn); else { @@ -883,6 +947,26 @@ static CURLcode pop3_state_auth_final_resp(struct connectdata *conn, return result; } +static CURLcode pop3_state_apop_resp(struct connectdata *conn, + int pop3code, + pop3state instate) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + + (void)instate; /* no use for this yet */ + + if(pop3code != '+') { + failf(data, "Authentication failed: %d", pop3code); + result = CURLE_LOGIN_DENIED; + } + + /* End of connect phase */ + state(conn, POP3_STOP); + + return result; +} + /* For USER responses */ static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code, @@ -1100,6 +1184,10 @@ static CURLcode pop3_statemach_act(struct connectdata *conn) result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state); break; + case POP3_APOP: + result = pop3_state_apop_resp(conn, pop3code, pop3c->state); + break; + case POP3_USER: result = pop3_state_user_resp(conn, pop3code, pop3c->state); break; @@ -1408,6 +1496,9 @@ static CURLcode pop3_disconnect(struct connectdata *conn, Curl_pp_disconnect(&pop3c->pp); + /* Clear our variables */ + Curl_safefree(pop3c->apoptimestamp); + /* Cleanup the SASL module */ Curl_sasl_cleanup(conn, pop3c->authused); diff --git a/lib/pop3.h b/lib/pop3.h index 1ca8bb289..1b6859955 100644 --- a/lib/pop3.h +++ b/lib/pop3.h @@ -40,6 +40,7 @@ typedef enum { POP3_AUTH_NTLM, POP3_AUTH_NTLM_TYPE2MSG, POP3_AUTH, + POP3_APOP, POP3_USER, POP3_PASS, POP3_COMMAND, @@ -60,6 +61,7 @@ struct pop3_conn { unsigned int authtypes; /* Supported authentication types */ unsigned int authmechs; /* Accepted SASL authentication mechanisms */ unsigned int authused; /* SASL auth mechanism used for the connection */ + char *apoptimestamp; /* APOP timestamp from the server greeting */ pop3state state; /* Always use pop3.c:state() to change state! */ };