pop3: Added support for apop authentication
authorSteve Holme <steve_holme@hotmail.com>
Sat, 9 Jun 2012 12:49:37 +0000 (13:49 +0100)
committerSteve Holme <steve_holme@hotmail.com>
Sat, 9 Jun 2012 12:49:37 +0000 (13:49 +0100)
RELEASE-NOTES
lib/pop3.c
lib/pop3.h

index fa85b19..80bed04 100644 (file)
@@ -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:
 
index 1e399a0..8ce7f25 100644 (file)
@@ -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 <curl/mprintf.h>
@@ -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);
 
index 1ca8bb2..1b68599 100644 (file)
@@ -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! */
 };