]> granicus.if.org Git - curl/commitdiff
pop3: Added support for SASL based authentication mechanism detection
authorSteve Holme <steve_holme@hotmail.com>
Sun, 27 May 2012 18:09:38 +0000 (19:09 +0100)
committerSteve Holme <steve_holme@hotmail.com>
Sun, 27 May 2012 18:09:38 +0000 (19:09 +0100)
Added support for detecting the supported SASL authentication mechanisms
via the AUTH command. There are two ways of detecting them, either by
using the AUTH command, that will return -ERR if not supported or by
using the CAPA command which will return SASL and the list of mechanisms
if supported, not include SASL if SASL authentication is not supported
or -ERR if the CAPA command is not supported. As such it seems simpler
to use the AUTH command and fallback to normal clear text authentication
if the the command is not supported.

Additionally updated the test cases to return -ERR when the AUTH command
is encountered. Additional test cases will be added when support for the
individual authentication mechanisms is added.

lib/pop3.c
lib/pop3.h
tests/data/test808
tests/data/test809
tests/data/test810
tests/data/test811
tests/data/test812
tests/data/test813
tests/data/test815

index ae540378f602d978c27e2cada52d39c9a5fda923..2f47fd1a9e65aa2d81157ba403312acf8a8966bc 100644 (file)
@@ -79,6 +79,7 @@
 #include "url.h"
 #include "rawstr.h"
 #include "strtoofft.h"
+#include "curl_sasl.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -208,11 +209,15 @@ 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 */
+   given string, but also detects the allowed authentication mechanisms
+   according to the AUTH response. */
 static int pop3_endofresp(struct pingpong *pp, int *resp)
 {
   char *line = pp->linestart_resp;
   size_t len = pp->nread_resp;
+  struct connectdata *conn = pp->conn;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  size_t wordlen;
 
   if((len < 3 || memcmp("+OK", line, 3)) &&
      (len < 4 || memcmp("-ERR", line, 4)))
@@ -220,6 +225,46 @@ static int pop3_endofresp(struct pingpong *pp, int *resp)
 
   *resp = line[1]; /* O or E */
 
+  if(pop3c->state == POP3_AUTH && len >= 3 && !memcmp(line, "+OK", 3)) {
+    line += 3;
+    len -= 3;
+
+    for(;;) {
+      while(len &&
+            (*line == ' ' || *line == '\t' ||
+             *line == '\r' || *line == '\n')) {
+        line++;
+        len--;
+      }
+
+      if(!len || *line == '.')
+        break;
+
+      for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
+            line[wordlen] != '\t' && line[wordlen] != '\r' &&
+            line[wordlen] != '\n';)
+        wordlen++;
+
+      if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
+        pop3c->authmechs |= SASL_AUTH_LOGIN;
+      else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
+        pop3c->authmechs |= SASL_AUTH_PLAIN;
+      else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
+        pop3c->authmechs |= SASL_AUTH_CRAM_MD5;
+      else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
+        pop3c->authmechs |= SASL_AUTH_DIGEST_MD5;
+      else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
+        pop3c->authmechs |= SASL_AUTH_GSSAPI;
+      else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
+        pop3c->authmechs |= SASL_AUTH_EXTERNAL;
+      else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
+        pop3c->authmechs |= SASL_AUTH_NTLM;
+
+      line += wordlen;
+      len -= wordlen;
+    }
+  }
+
   return TRUE;
 }
 
@@ -231,6 +276,7 @@ static void state(struct connectdata *conn, pop3state newstate)
   static const char * const names[]={
     "STOP",
     "SERVERGREET",
+    "AUTH",
     "USER",
     "PASS",
     "STARTTLS",
@@ -248,6 +294,24 @@ static void state(struct connectdata *conn, pop3state newstate)
   pop3c->state = newstate;
 }
 
+static CURLcode pop3_state_auth(struct connectdata *conn)
+{
+  CURLcode result;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+
+  pop3c->authmechs = 0;         /* No known authentication mechanisms yet */
+
+  /* send AUTH */
+  result = Curl_pp_sendf(&pop3c->pp, "AUTH");
+
+  if(result)
+    return result;
+
+  state(conn, POP3_AUTH);
+
+  return CURLE_OK;
+}
+
 static CURLcode pop3_state_user(struct connectdata *conn)
 {
   CURLcode result;
@@ -303,7 +367,7 @@ static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
     state(conn, POP3_STARTTLS);
   }
   else
-    result = pop3_state_user(conn);
+    result = pop3_state_auth(conn);
 
   return result;
 }
@@ -325,14 +389,14 @@ static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
       state(conn, POP3_STOP);
     }
     else
-      result = pop3_state_user(conn);
+      result = pop3_state_auth(conn);
   }
   else {
     /* Curl_ssl_connect is BLOCKING */
     result = Curl_ssl_connect(conn, FIRSTSOCKET);
     if(CURLE_OK == result) {
       pop3_to_pop3s(conn);
-      result = pop3_state_user(conn);
+      result = pop3_state_auth(conn);
     }
     else {
       state(conn, POP3_STOP);
@@ -342,6 +406,23 @@ static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
   return result;
 }
 
+/* For AUTH responses */
+static CURLcode pop3_state_auth_resp(struct connectdata *conn,
+                                     int pop3code,
+                                     pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct FTP *pop3 = data->state.proto.pop3;
+
+  (void)instate; /* no use for this yet */
+
+  /* Proceed with clear text authentication as we used to for now */
+  result = pop3_state_user(conn);
+
+  return result;
+}
+
 /* For USER responses */
 static CURLcode pop3_state_user_resp(struct connectdata *conn,
                                      int pop3code,
@@ -506,6 +587,10 @@ static CURLcode pop3_statemach_act(struct connectdata *conn)
       result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
       break;
 
+    case POP3_AUTH:
+      result = pop3_state_auth_resp(conn, pop3code, pop3c->state);
+      break;
+
     case POP3_USER:
       result = pop3_state_user_resp(conn, pop3code, pop3c->state);
       break;
index 99b514e4ca9b86c67bee58ede3db6ea6f4262058..01701e4d7bbf994ede20c37c77957ed64d80ec96 100644 (file)
@@ -29,6 +29,7 @@ typedef enum {
   POP3_STOP,         /* do nothing state, stops the state machine */
   POP3_SERVERGREET,  /* waiting for the initial greeting immediately after
                         a connect */
+  POP3_AUTH,
   POP3_USER,
   POP3_PASS,
   POP3_STARTTLS,
@@ -46,6 +47,7 @@ struct pop3_conn {
   size_t eob;        /* number of bytes of the EOB (End Of Body) that has been
                         received thus far */
   size_t strip;      /* number of bytes from the start to ignore as non-body */
+  unsigned int authmechs; /* Accepted authentication methods */
   pop3state state;   /* always use pop3.c:state() to change state! */
 };
 
index 756801c6be22ca72f34657c8899f28d0e748c633..57e1382b1396ed1182c2271601cb394a4e5e3383 100644 (file)
@@ -10,6 +10,7 @@ LIST
 # Server-side
 <reply>
 <servercmd>
+REPLY AUTH -ERR unsupported command\r
 REPLY LIST +OK 808 100
 </servercmd>
 </reply>
@@ -32,6 +33,7 @@ pop3://%HOSTIP:%POP3PORT/808 -l -u user:secret
 # Verify data after the test has been "shot"
 <verify>
 <protocol>
+AUTH\r
 USER user\r
 PASS secret\r
 LIST 808\r
index e9f22dd831e85ae36bf6baa7a16c838d0a5f3378..7cd04c891ab20db89c76822320cecbe5f7b84741 100644 (file)
@@ -11,6 +11,7 @@ FAILURE
 # Server-side
 <reply>
 <servercmd>
+REPLY AUTH -ERR unsupported command\r
 REPLY LIST -ERR no such message
 </servercmd>
 </reply>
@@ -36,6 +37,7 @@ pop3://%HOSTIP:%POP3PORT/809 -l -u user:secret
 56
 </errorcode>
 <protocol>
+AUTH\r
 USER user\r
 PASS secret\r
 LIST 809\r
index 09f9562a37393527f88e34300be3292e06844cfa..2e24c295b65c6387ea64eeda37eabedf5bcd5a65 100644 (file)
@@ -13,6 +13,7 @@ LIST
 # packets
 <servercmd>
 SLOWDOWN
+REPLY AUTH -ERR unsupported command\r
 </servercmd>
 # When doing LIST, we get the default list output hard-coded in the test
 # POP3 server
@@ -41,6 +42,7 @@ pop3://%HOSTIP:%POP3PORT/ -u user:secret
 # Verify data after the test has been "shot"
 <verify>
 <protocol>
+AUTH\r
 USER user\r
 PASS secret\r
 LIST\r
index ca107abccc8b984887ba33198f13d071bac98fda..06fa57dcb4abd4ed6526fa78ca5e0c2cdc0a174f 100644 (file)
@@ -10,6 +10,7 @@ LIST
 # Server-side
 <reply>
 <servercmd>
+REPLY AUTH -ERR unsupported command\r
 REPLY LIST +OK but no messages\r\n.
 </servercmd>
 <datacheck>
@@ -34,6 +35,7 @@ pop3://%HOSTIP:%POP3PORT/ -u user:secret
 # Verify data after the test has been "shot"
 <verify>
 <protocol>
+AUTH\r
 USER user\r
 PASS secret\r
 LIST\r
index eb2a836695bc4bee975c3b4d0bd49e24f46e6a07..9f1ba92964db972e9c9efbd535ea370e371dda63 100644 (file)
@@ -11,6 +11,7 @@ FAILURE
 # Server-side
 <reply>
 <servercmd>
+REPLY AUTH -ERR unsupported command\r
 REPLY RETR -ERR no such message
 </servercmd>
 </reply>
@@ -36,6 +37,7 @@ pop3://%HOSTIP:%POP3PORT/812 -u user:secret
 56
 </errorcode>
 <protocol>
+AUTH\r
 USER user\r
 PASS secret\r
 RETR 812\r
index 4953af413bd9cf715f8b9d34af2fe755ea165f6d..13cfd7074be31b254c42ee627b11977ee3a9be50 100644 (file)
@@ -10,6 +10,7 @@ FAILURE
 # Server-side
 <reply>
 <servercmd>
+REPLY AUTH -ERR unsupported command\r
 REPLY PASS -ERR invalid login
 </servercmd>
 </reply>
@@ -35,6 +36,7 @@ pop3://%HOSTIP:%POP3PORT/813 -u user:wrong
 67
 </errorcode>
 <protocol>
+AUTH\r
 USER user\r
 PASS wrong\r
 QUIT\r
index df7b1b6dd180f8c606591cee19b8104834f4e616..9779fe690e56e4109ccb2a0ca73089774cc195af 100644 (file)
@@ -9,6 +9,9 @@ RETR
 #
 # Server-side
 <reply>
+<servercmd>\r
+REPLY AUTH -ERR unsupported command\r
+</servercmd>\r
 <data>
 From: me@somewhere\r
 To: fake@nowhere\r
@@ -49,6 +52,7 @@ pop3://%HOSTIP:%POP3PORT/815 -u user:secret
 # Verify data after the test has been "shot"
 <verify>
 <protocol>
+AUTH\r
 USER user\r
 PASS secret\r
 RETR 815\r