]> granicus.if.org Git - curl/commitdiff
SASL: common state engine for imap/pop3/smtp
authorPatrick Monnerat <pm@datasphere.ch>
Tue, 20 Jan 2015 16:33:05 +0000 (17:33 +0100)
committerPatrick Monnerat <pm@datasphere.ch>
Tue, 20 Jan 2015 16:33:05 +0000 (17:33 +0100)
lib/curl_sasl.c
lib/curl_sasl.h
lib/imap.c
lib/imap.h
lib/pop3.c
lib/pop3.h
lib/smtp.c
lib/smtp.h

index 7cb575b33b07dfba9e72765846bdeb823650a860..6a648f0ae1db222f56197f40092a874526aff4d5 100644 (file)
@@ -43,6 +43,7 @@
 #include "strtok.h"
 #include "strequal.h"
 #include "rawstr.h"
+#include "sendf.h"
 #include "non-ascii.h" /* included for Curl_convert_... prototypes */
 
 #define _MPRINTF_REPLACE /* use our functions only */
@@ -1271,11 +1272,339 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
  *
  * Initializes an SASL structure.
  */
-void Curl_sasl_init(struct SASL *sasl)
+void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params)
 {
+  sasl->params = params;           /* Set protocol dependent parameters */
+  sasl->state = SASL_STOP;         /* Not yet running */
   sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */
   sasl->prefmech = SASL_AUTH_ANY;  /* Prefer all mechanisms */
   sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */
   sasl->resetprefs = TRUE;         /* Reset prefmech upon AUTH parsing. */
   sasl->mutual_auth = FALSE;       /* No mutual authentication (GSSAPI only) */
+  sasl->force_ir = FALSE;          /* Respect external option */
+}
+
+/*
+ * state()
+ *
+ * This is the ONLY way to change SASL state!
+ */
+static void state(struct SASL *sasl,
+                  struct connectdata *conn, saslstate newstate)
+{
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  /* for debug purposes */
+  static const char * const names[]={
+    "STOP",
+    "PLAIN",
+    "LOGIN",
+    "LOGIN_PASSWD",
+    "CRAMMD5",
+    "DIGESTMD5",
+    "DIGESTMD5_RESP",
+    "NTLM",
+    "NTLM_TYPE2MSG",
+    "GSSAPI",
+    "GSSAPI_TOKEN",
+    "GSSAPI_NO_DATA",
+    "XOAUTH2",
+    "CANCEL",
+    "FINAL",
+    /* LAST */
+  };
+
+  if(sasl->state != newstate)
+    infof(conn->data, "SASL %p state change from %s to %s\n",
+          (void *)sasl, names[sasl->state], names[newstate]);
+#endif
+
+  sasl->state = newstate;
+}
+
+/*
+ * Curl_sasl_start()
+ *
+ * Calculate the required login details for SASL authentication.
+ */
+CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
+                         bool force_ir, saslprogress *progress)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  unsigned int enabledmechs;
+  const char *mech = NULL;
+  char *resp = NULL;
+  size_t len = 0;
+  saslstate state1 = SASL_STOP;
+  saslstate state2 = SASL_FINAL;
+
+  sasl->force_ir = force_ir;    /* Latch for future use */
+  sasl->authused = 0;           /* No mechanism used yet */
+  enabledmechs = sasl->authmechs & sasl->prefmech;
+  *progress = SASL_IDLE;
+
+  /* Calculate the supported authentication mechanism, by decreasing order of
+   *      security, as well as the initial response where appropriate */
+#if defined(USE_KERBEROS5)
+  if(enabledmechs & SASL_MECH_GSSAPI) {
+    sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */
+    mech = SASL_MECH_STRING_GSSAPI;
+    state1 = SASL_GSSAPI;
+    state2 = SASL_GSSAPI_TOKEN;
+    sasl->authused = SASL_MECH_GSSAPI;
+
+    if(force_ir || data->set.sasl_ir)
+      result = Curl_sasl_create_gssapi_user_message(data, conn->user,
+                                                    conn->passwd,
+                                                    sasl->params->service,
+                                                    sasl->mutual_auth,
+                                                    NULL, &conn->krb5,
+                                                    &resp, &len);
+  }
+  else
+#endif
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+  if(enabledmechs & SASL_MECH_DIGEST_MD5) {
+    mech = SASL_MECH_STRING_DIGEST_MD5;
+    state1 = SASL_DIGESTMD5;
+    sasl->authused = SASL_MECH_DIGEST_MD5;
+  }
+  else if(enabledmechs & SASL_MECH_CRAM_MD5) {
+    mech = SASL_MECH_STRING_CRAM_MD5;
+    state1 = SASL_CRAMMD5;
+    sasl->authused = SASL_MECH_CRAM_MD5;
+  }
+  else
+#endif
+#ifdef USE_NTLM
+  if(enabledmechs & SASL_MECH_NTLM) {
+    mech = SASL_MECH_STRING_NTLM;
+    state1 = SASL_NTLM;
+    state2 = SASL_NTLM_TYPE2MSG;
+    sasl->authused = SASL_MECH_NTLM;
+
+    if(force_ir || data->set.sasl_ir)
+      result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
+                                                   &conn->ntlm, &resp, &len);
+    }
+  else
+#endif
+  if((((enabledmechs & SASL_MECH_XOAUTH2) &&
+      sasl->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
+    mech = SASL_MECH_STRING_XOAUTH2;
+    state1 = SASL_XOAUTH2;
+    sasl->authused = SASL_MECH_XOAUTH2;
+
+    if(force_ir || data->set.sasl_ir)
+      result = Curl_sasl_create_xoauth2_message(data, conn->user,
+                                                conn->xoauth2_bearer,
+                                                &resp, &len);
+  }
+  else if(enabledmechs & SASL_MECH_LOGIN) {
+    mech = SASL_MECH_STRING_LOGIN;
+    state1 = SASL_LOGIN;
+    state2 = SASL_LOGIN_PASSWD;
+    sasl->authused = SASL_MECH_LOGIN;
+
+    if(force_ir || data->set.sasl_ir)
+      result = Curl_sasl_create_login_message(data, conn->user, &resp, &len);
+  }
+  else if(enabledmechs & SASL_MECH_PLAIN) {
+    mech = SASL_MECH_STRING_PLAIN;
+    state1 = SASL_PLAIN;
+    sasl->authused = SASL_MECH_PLAIN;
+
+    if(force_ir || data->set.sasl_ir)
+      result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
+                                              &resp, &len);
+  }
+  else
+    state2 = SASL_STOP;    /* No authentication started */
+
+  if(!result) {
+    if(resp && sasl->params->maxirlen &&
+       strlen(mech) + len > sasl->params->maxirlen) {
+      Curl_safefree(resp);
+      resp = NULL;
+    }
+    if(mech) {
+      result = sasl->params->sendauth(conn, mech, resp);
+      if(!result) {
+        *progress = SASL_INPROGRESS;
+        state(sasl, conn, resp? state2: state1);
+      }
+    }
+  }
+
+  Curl_safefree(resp);
+  return result;
+}
+
+/*
+ * Curl_sasl_continue()
+ *
+ * Continue an SASL authentication.
+ */
+CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
+                         int code, saslprogress *progress)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  saslstate newstate = SASL_FINAL;
+  char *chlg = NULL;
+  char *resp = NULL;
+  char *serverdata;
+  size_t len = 0;
+  size_t chlglen = 0;
+
+  *progress = SASL_INPROGRESS;
+
+  if(sasl->state == SASL_FINAL) {
+    if(code != sasl->params->finalcode)
+      result = CURLE_LOGIN_DENIED;
+    *progress = SASL_DONE;
+    state(sasl, conn, SASL_STOP);
+    return result;
+  }
+
+  if(sasl->state != SASL_CANCEL && code != sasl->params->contcode) {
+    *progress = SASL_DONE;
+    state(sasl, conn, SASL_STOP);
+    return CURLE_LOGIN_DENIED;
+  }
+
+  switch(sasl->state) {
+  case SASL_STOP:
+    *progress = SASL_DONE;
+    return result;
+  case SASL_PLAIN:
+    result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
+                                            &resp, &len);
+    break;
+  case SASL_LOGIN:
+    result = Curl_sasl_create_login_message(data, conn->user, &resp, &len);
+    newstate = SASL_LOGIN_PASSWD;
+    break;
+  case SASL_LOGIN_PASSWD:
+    result = Curl_sasl_create_login_message(data, conn->passwd, &resp, &len);
+    break;
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+  case SASL_CRAMMD5:
+    sasl->params->getmessage(data->state.buffer, &serverdata);
+    result = Curl_sasl_decode_cram_md5_message(serverdata, &chlg, &chlglen);
+    if(!result)
+      result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
+                                                 conn->passwd, &resp, &len);
+    Curl_safefree(chlg);
+    break;
+  case SASL_DIGESTMD5:
+    sasl->params->getmessage(data->state.buffer, &serverdata);
+    result = Curl_sasl_create_digest_md5_message(data, serverdata,
+                                                 conn->user, conn->passwd,
+                                                 sasl->params->service,
+                                                 &resp, &len);
+    newstate = SASL_DIGESTMD5_RESP;
+    break;
+  case SASL_DIGESTMD5_RESP:
+    if(!(resp = strdup("")))
+      result = CURLE_OUT_OF_MEMORY;
+    break;
+#endif
+
+#ifdef USE_NTLM
+  case SASL_NTLM:
+    /* Create the type-1 message */
+    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
+                                                 &conn->ntlm,
+                                                 &resp, &len);
+    newstate = SASL_NTLM_TYPE2MSG;
+    break;
+  case SASL_NTLM_TYPE2MSG:
+    /* Decode the type-2 message */
+    sasl->params->getmessage(data->state.buffer, &serverdata);
+    result = Curl_sasl_decode_ntlm_type2_message(data,
+                                                 serverdata, &conn->ntlm);
+    if(!result)
+      result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
+                                                   conn->passwd, &conn->ntlm,
+                                                   &resp, &len);
+    break;
+#endif
+
+#if defined(USE_KERBEROS5)
+  case SASL_GSSAPI:
+    result = Curl_sasl_create_gssapi_user_message(data, conn->user,
+                                                  conn->passwd,
+                                                  sasl->params->service,
+                                                  sasl->mutual_auth, NULL,
+                                                  &conn->krb5,
+                                                  &resp, &len);
+    newstate = SASL_GSSAPI_TOKEN;
+    break;
+  case SASL_GSSAPI_TOKEN:
+    sasl->params->getmessage(data->state.buffer, &serverdata);
+    if(sasl->mutual_auth) {
+      /* Decode the user token challenge and create the optional response
+         message */
+      result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL,
+                                                    sasl->mutual_auth,
+                                                    serverdata, &conn->krb5,
+                                                    &resp, &len);
+      newstate = SASL_GSSAPI_NO_DATA;
+    }
+    else
+      /* Decode the security challenge and create the response message */
+      result = Curl_sasl_create_gssapi_security_message(data, serverdata,
+                                                        &conn->krb5,
+                                                        &resp, &len);
+    break;
+  case SASL_GSSAPI_NO_DATA:
+    sasl->params->getmessage(data->state.buffer, &serverdata);
+    /* Decode the security challenge and create the response message */
+    result = Curl_sasl_create_gssapi_security_message(data, serverdata,
+                                                      &conn->krb5,
+                                                      &resp, &len);
+    break;
+#endif
+
+  case SASL_XOAUTH2:
+    /* Create the authorisation message */
+    result = Curl_sasl_create_xoauth2_message(data, conn->user,
+                                              conn->xoauth2_bearer,
+                                              &resp, &len);
+    break;
+  case SASL_CANCEL:
+    /* Remove the offending mechanism from the supported list */
+    sasl->authmechs ^= sasl->authused;
+
+    /* Start an alternative SASL authentication */
+    result = Curl_sasl_start(sasl, conn, sasl->force_ir, progress);
+    newstate = sasl->state;   /* Use state from Curl_sasl_start() */
+    break;
+  default:
+    failf(data, "Unsupported SASL authentication mechanism");
+    result = CURLE_UNSUPPORTED_PROTOCOL;  /* Should not happen */
+    break;
+  }
+
+  switch(result) {
+  case CURLE_BAD_CONTENT_ENCODING:
+    /* Cancel dialog */
+    result = sasl->params->sendcont(conn, "*");
+    newstate = SASL_CANCEL;
+    break;
+  case CURLE_OK:
+    if(resp)
+      result = sasl->params->sendcont(conn, resp);
+    break;
+  default:
+    newstate = SASL_STOP;    /* Stop on error */
+    *progress = SASL_DONE;
+    break;
+  }
+
+  Curl_safefree(resp);
+  state(sasl, conn, newstate);
+  return result;
 }
index e043449db44cab00c1250c227b0ecae310871a9f..19fa239e4387cedf0bca72e5c431f6b61080b2b7 100644 (file)
@@ -68,13 +68,57 @@ enum {
   CURLDIGESTALGO_MD5SESS
 };
 
+/* SASL machine states */
+typedef enum {
+  SASL_STOP,
+  SASL_PLAIN,
+  SASL_LOGIN,
+  SASL_LOGIN_PASSWD,
+  SASL_CRAMMD5,
+  SASL_DIGESTMD5,
+  SASL_DIGESTMD5_RESP,
+  SASL_NTLM,
+  SASL_NTLM_TYPE2MSG,
+  SASL_GSSAPI,
+  SASL_GSSAPI_TOKEN,
+  SASL_GSSAPI_NO_DATA,
+  SASL_XOAUTH2,
+  SASL_CANCEL,
+  SASL_FINAL
+} saslstate;
+
+/* Progress indicator */
+typedef enum {
+  SASL_IDLE,
+  SASL_INPROGRESS,
+  SASL_DONE
+} saslprogress;
+
+/* Protocol dependent SASL parameters */
+struct SASLproto {
+  const char *service;     /* The service name */
+  int contcode;            /* Code to receive when continuation is expected */
+  int finalcode;           /* Code to receive upon authentication success */
+  size_t maxirlen;         /* Maximum initial response length */
+  CURLcode (*sendauth)(struct connectdata *conn,
+                       const char *mech, const char *ir);
+                           /* Send authentication command */
+  CURLcode (*sendcont)(struct connectdata *conn, const char *contauth);
+                           /* Send authentication continuation */
+  void (*getmessage)(char *buffer, char **outptr);
+                           /* Get SASL response message */
+};
+
 /* Per-connection parameters */
 struct SASL {
+  const struct SASLproto *params; /* Protocol dependent parameters */
+  saslstate state;         /* Current machine state */
   unsigned int authmechs;  /* Accepted authentication mechanisms */
   unsigned int prefmech;   /* Preferred authentication mechanism */
   unsigned int authused;   /* Auth mechanism used for the connection */
   bool resetprefs;         /* For URL auth option parsing. */
   bool mutual_auth;        /* Mutual authentication enabled (GSSAPI only) */
+  bool force_ir;           /* Protocol always supports initial response */
 };
 
 /* This is used to test whether the line starts with the given mechanism */
@@ -211,6 +255,14 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
                                          const char *value, size_t len);
 
 /* Initializes an SASL structure */
-void Curl_sasl_init(struct SASL *sasl);
+void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params);
+
+/* Calculate the required login details for SASL authentication  */
+CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
+                         bool force_ir, saslprogress *progress);
+
+/* Continue an SASL authentication  */
+CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
+                         int code, saslprogress *progress);
 
 #endif /* HEADER_CURL_SASL_H */
index 965efdf8a089cca7ffd976316de12f9a6ff6376d..c5c8e4aeafff7b7fd255484318f92e79365e1e06 100644 (file)
@@ -105,10 +105,12 @@ static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
 static CURLcode imap_parse_url_options(struct connectdata *conn);
 static CURLcode imap_parse_url_path(struct connectdata *conn);
 static CURLcode imap_parse_custom_request(struct connectdata *conn);
-static CURLcode imap_calc_sasl_details(struct connectdata *conn,
-                                       const char **mech,
-                                       char **initresp, size_t *len,
-                                       imapstate *state1, imapstate *state2);
+static CURLcode imap_perform_authenticate(struct connectdata *conn,
+                                          const char *mech,
+                                          const char *initresp);
+static CURLcode imap_continue_authenticate(struct connectdata *conn,
+                                           const char *resp);
+static void imap_get_message(char *buffer, char** outptr);
 
 /*
  * IMAP protocol handler.
@@ -213,6 +215,18 @@ static const struct Curl_handler Curl_handler_imaps_proxy = {
 #endif
 #endif
 
+/* SASL parameters for the imap protocol */
+static const struct SASLproto saslimap = {
+  "imap",                     /* The service name */
+  '+',                        /* Code received when continuation is expected */
+  'O',                        /* Code to receive upon authentication success */
+  0,                          /* Maximum initial response length (no max) */
+  imap_perform_authenticate,  /* Send authentication command */
+  imap_continue_authenticate, /* Send authentication continuation */
+  imap_get_message            /* Get SASL response message */
+};
+
+
 #ifdef USE_SSL
 static void imap_to_imaps(struct connectdata *conn)
 {
@@ -353,16 +367,7 @@ static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
      (len >= 2 && !memcmp("+ ", line, 2))) {
     switch(imapc->state) {
       /* States which are interested in continuation responses */
-      case IMAP_AUTHENTICATE_PLAIN:
-      case IMAP_AUTHENTICATE_LOGIN:
-      case IMAP_AUTHENTICATE_LOGIN_PASSWD:
-      case IMAP_AUTHENTICATE_CRAMMD5:
-      case IMAP_AUTHENTICATE_DIGESTMD5:
-      case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
-      case IMAP_AUTHENTICATE_NTLM:
-      case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
-      case IMAP_AUTHENTICATE_XOAUTH2:
-      case IMAP_AUTHENTICATE_FINAL:
+      case IMAP_AUTHENTICATE:
       case IMAP_APPEND:
         *resp = '+';
         break;
@@ -425,20 +430,7 @@ static void state(struct connectdata *conn, imapstate newstate)
     "CAPABILITY",
     "STARTTLS",
     "UPGRADETLS",
-    "AUTHENTICATE_PLAIN",
-    "AUTHENTICATE_LOGIN",
-    "AUTHENTICATE_LOGIN_PASSWD",
-    "AUTHENTICATE_CRAMMD5",
-    "AUTHENTICATE_DIGESTMD5",
-    "AUTHENTICATE_DIGESTMD5_RESP",
-    "AUTHENTICATE_NTLM",
-    "AUTHENTICATE_NTLM_TYPE2MSG",
-    "AUTHENTICATE_GSSAPI",
-    "AUTHENTICATE_GSSAPI_TOKEN",
-    "AUTHENTICATE_GSSAPI_NO_DATA",
-    "AUTHENTICATE_XOAUTH2",
-    "AUTHENTICATE_CANCEL",
-    "AUTHENTICATE_FINAL",
+    "AUTHENTICATE",
     "LOGIN",
     "LIST",
     "SELECT",
@@ -576,29 +568,36 @@ static CURLcode imap_perform_login(struct connectdata *conn)
  */
 static CURLcode imap_perform_authenticate(struct connectdata *conn,
                                           const char *mech,
-                                          const char *initresp,
-                                          imapstate state1, imapstate state2)
+                                          const char *initresp)
 {
   CURLcode result = CURLE_OK;
 
   if(initresp) {
     /* Send the AUTHENTICATE command with the initial response */
     result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
-
-    if(!result)
-      state(conn, state2);
   }
   else {
     /* Send the AUTHENTICATE command */
     result = imap_sendf(conn, "AUTHENTICATE %s", mech);
-
-    if(!result)
-      state(conn, state1);
   }
 
   return result;
 }
 
+/***********************************************************************
+ *
+ * imap_continue_authenticate()
+ *
+ * Sends SASL continuation data or cancellation.
+ */
+static CURLcode imap_continue_authenticate(struct connectdata *conn,
+                                           const char *resp)
+{
+  struct imap_conn *imapc = &conn->proto.imapc;
+
+  return Curl_pp_sendf(&imapc->pp, "%s", resp);
+}
+
 /***********************************************************************
  *
  * imap_perform_authentication()
@@ -611,31 +610,22 @@ static CURLcode imap_perform_authentication(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
   struct imap_conn *imapc = &conn->proto.imapc;
-  const char *mech = NULL;
-  char *initresp = NULL;
-  size_t len = 0;
-  imapstate state1 = IMAP_STOP;
-  imapstate state2 = IMAP_STOP;
+  saslprogress progress;
 
   /* Check we have a username and password to authenticate with and end the
      connect phase if we don't */
   if(!conn->bits.user_passwd) {
     state(conn, IMAP_STOP);
-
     return result;
   }
 
   /* Calculate the SASL login details */
-  result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
-                                  &state2);
+  result = Curl_sasl_start(&imapc->sasl, conn, imapc->ir_supported, &progress);
 
   if(!result) {
-    if(mech && (imapc->preftype & IMAP_TYPE_SASL)) {
-      /* Perform SASL based authentication */
-      result = imap_perform_authenticate(conn, mech, initresp, state1, state2);
-    }
-    else if((!imapc->login_disabled) &&
-            (imapc->preftype & IMAP_TYPE_CLEARTEXT))
+    if(progress == SASL_INPROGRESS)
+      state(conn, IMAP_AUTHENTICATE);
+    else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
       /* Perform clear text authentication */
       result = imap_perform_login(conn);
     else {
@@ -645,8 +635,6 @@ static CURLcode imap_perform_authentication(struct connectdata *conn)
     }
   }
 
-  Curl_safefree(initresp);
-
   return result;
 }
 
@@ -976,569 +964,36 @@ static CURLcode imap_state_starttls_resp(struct connectdata *conn,
   return result;
 }
 
-/* For AUTHENTICATE PLAIN (without initial response) responses */
-static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
-                                           int imapcode,
-                                           imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *plainauth = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != '+') {
-    failf(data, "Access denied. %c", imapcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the authorisation message */
-    result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
-                                            &plainauth, &len);
-    if(!result && plainauth) {
-      /* Send the message */
-      result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
-
-      if(!result)
-        state(conn, IMAP_AUTHENTICATE_FINAL);
-    }
-  }
-
-  Curl_safefree(plainauth);
-
-  return result;
-}
-
-/* For AUTHENTICATE LOGIN (without initial response) responses */
-static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
-                                           int imapcode,
-                                           imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *authuser = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != '+') {
-    failf(data, "Access denied: %d", imapcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the user message */
-    result = Curl_sasl_create_login_message(data, conn->user,
-                                            &authuser, &len);
-    if(!result && authuser) {
-      /* Send the user */
-      result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
-
-      if(!result)
-        state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
-    }
-  }
-
-  Curl_safefree(authuser);
-
-  return result;
-}
-
-/* For AUTHENTICATE LOGIN user entry responses */
-static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
-                                                    int imapcode,
-                                                    imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *authpasswd = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != '+') {
-    failf(data, "Access denied: %d", imapcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the password message */
-    result = Curl_sasl_create_login_message(data, conn->passwd,
-                                            &authpasswd, &len);
-    if(!result && authpasswd) {
-      /* Send the password */
-      result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
-
-      if(!result)
-        state(conn, IMAP_AUTHENTICATE_FINAL);
-    }
-  }
-
-  Curl_safefree(authpasswd);
-
-  return result;
-}
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-/* For AUTHENTICATE CRAM-MD5 responses */
-static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
-                                          int imapcode,
-                                          imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlg = NULL;
-  char *chlg64 = NULL;
-  char *rplyb64 = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != '+') {
-    failf(data, "Access denied: %d", imapcode);
-    return CURLE_LOGIN_DENIED;
-  }
-
-  /* Get the challenge message */
-  imap_get_message(data->state.buffer, &chlg64);
-
-  /* Decode the challenge message */
-  result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
-  if(result) {
-    /* Send the cancellation */
-    result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
-
-    if(!result)
-      state(conn, IMAP_AUTHENTICATE_CANCEL);
-  }
-  else {
-    /* Create the response message */
-    result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
-                                               conn->passwd, &rplyb64, &len);
-    if(!result && rplyb64) {
-      /* Send the response */
-      result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
-
-      if(!result)
-        state(conn, IMAP_AUTHENTICATE_FINAL);
-    }
-  }
-
-  Curl_safefree(chlg);
-  Curl_safefree(rplyb64);
-
-  return result;
-}
-
-/* For AUTHENTICATE DIGEST-MD5 challenge responses */
-static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
-                                            int imapcode,
-                                            imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlg64 = NULL;
-  char *rplyb64 = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != '+') {
-    failf(data, "Access denied: %d", imapcode);
-    return CURLE_LOGIN_DENIED;
-  }
-
-  /* Get the challenge message */
-  imap_get_message(data->state.buffer, &chlg64);
-
-  /* Create the response message */
-  result = Curl_sasl_create_digest_md5_message(data, chlg64,
-                                               conn->user, conn->passwd,
-                                               "imap", &rplyb64, &len);
-  if(result) {
-    if(result == CURLE_BAD_CONTENT_ENCODING) {
-      /* Send the cancellation */
-      result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
-
-      if(!result)
-        state(conn, IMAP_AUTHENTICATE_CANCEL);
-    }
-  }
-  else {
-    /* Send the response */
-    result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
-
-    if(!result)
-      state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
-  }
-
-  Curl_safefree(rplyb64);
-
-  return result;
-}
-
-/* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
-static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
-                                                 int imapcode,
-                                                 imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != '+') {
-    failf(data, "Authentication failed: %d", imapcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Send an empty response */
-    result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
-
-    if(!result)
-      state(conn, IMAP_AUTHENTICATE_FINAL);
-  }
-
-  return result;
-}
-#endif
-
-#ifdef USE_NTLM
-/* For AUTHENTICATE NTLM (without initial response) responses */
-static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
-                                          int imapcode,
-                                          imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *type1msg = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != '+') {
-    failf(data, "Access denied: %d", imapcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the type-1 message */
-    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
-                                                 &conn->ntlm,
-                                                 &type1msg, &len);
-    if(!result && type1msg) {
-      /* Send the message */
-      result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
-
-      if(!result)
-        state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
-    }
-  }
-
-  Curl_safefree(type1msg);
-
-  return result;
-}
-
-/* For NTLM type-2 responses (sent in reponse to our type-1 message) */
-static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
-                                                   int imapcode,
-                                                   imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *type2msg = NULL;
-  char *type3msg = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != '+') {
-    failf(data, "Access denied: %d", imapcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Get the challenge message */
-    imap_get_message(data->state.buffer, &type2msg);
-
-    /* Decode the type-2 message */
-    result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
-    if(result) {
-      /* Send the cancellation */
-      result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
-
-      if(!result)
-        state(conn, IMAP_AUTHENTICATE_CANCEL);
-    }
-    else {
-      /* Create the type-3 message */
-      result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
-                                                   conn->passwd, &conn->ntlm,
-                                                   &type3msg, &len);
-      if(!result && type3msg) {
-        /* Send the message */
-        result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
-
-        if(!result)
-          state(conn, IMAP_AUTHENTICATE_FINAL);
-      }
-    }
-  }
-
-  Curl_safefree(type3msg);
-
-  return result;
-}
-#endif
-
-#if defined(USE_KERBEROS5)
-/* For AUTHENTICATE GSSAPI (without initial response) responses */
-static CURLcode imap_state_auth_gssapi_resp(struct connectdata *conn,
-                                            int imapcode,
-                                            imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct imap_conn *imapc = &conn->proto.imapc;
-  size_t len = 0;
-  char *respmsg = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != '+') {
-    failf(data, "Access denied: %d", imapcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the initial response message */
-    result = Curl_sasl_create_gssapi_user_message(data, conn->user,
-                                                  conn->passwd, "imap",
-                                                  imapc->sasl.mutual_auth,
-                                                  NULL, &conn->krb5,
-                                                  &respmsg, &len);
-    if(!result && respmsg) {
-      /* Send the message */
-      result = Curl_pp_sendf(&imapc->pp, "%s", respmsg);
-
-      if(!result)
-        state(conn, IMAP_AUTHENTICATE_GSSAPI_TOKEN);
-    }
-  }
-
-  Curl_safefree(respmsg);
-
-  return result;
-}
-
-/* For AUTHENTICATE GSSAPI user token responses */
-static CURLcode imap_state_auth_gssapi_token_resp(struct connectdata *conn,
-                                                  int imapcode,
-                                                  imapstate instate)
+/* For SASL authentication responses */
+static CURLcode imap_state_auth_resp(struct connectdata *conn,
+                                     int imapcode,
+                                     imapstate instate)
 {
   CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
   struct imap_conn *imapc = &conn->proto.imapc;
-  char *chlgmsg = NULL;
-  char *respmsg = NULL;
-  size_t len = 0;
+  saslprogress progress;
 
   (void)instate; /* no use for this yet */
 
-  if(imapcode != '+') {
-    failf(data, "Access denied: %d", imapcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Get the challenge message */
-    imap_get_message(data->state.buffer, &chlgmsg);
-
-    if(imapc->sasl.mutual_auth)
-      /* Decode the user token challenge and create the optional response
-         message */
-      result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL,
-                                                    imapc->sasl.mutual_auth,
-                                                    chlgmsg, &conn->krb5,
-                                                    &respmsg, &len);
-    else
-      /* Decode the security challenge and create the response message */
-      result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
-                                                        &conn->krb5,
-                                                        &respmsg, &len);
-
-    if(result) {
-      if(result == CURLE_BAD_CONTENT_ENCODING) {
-        /* Send the cancellation */
-        result = Curl_pp_sendf(&imapc->pp, "%s", "*");
-
-        if(!result)
-          state(conn, IMAP_AUTHENTICATE_CANCEL);
-      }
-    }
-    else {
-      /* Send the response */
-      if(respmsg)
-        result = Curl_pp_sendf(&imapc->pp, "%s", respmsg);
-      else
-        result = Curl_pp_sendf(&imapc->pp, "%s", "");
-
-      if(!result)
-        state(conn, imapc->sasl.mutual_auth? IMAP_AUTHENTICATE_GSSAPI_NO_DATA:
-                                             IMAP_AUTHENTICATE_FINAL);
-    }
-  }
-
-  Curl_safefree(respmsg);
-
-  return result;
-}
-
-/* For AUTHENTICATE GSSAPI no data responses */
-static CURLcode imap_state_auth_gssapi_no_data_resp(struct connectdata *conn,
-                                                    int imapcode,
-                                                    imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlgmsg = NULL;
-  char *respmsg = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != '+') {
-    failf(data, "Access denied: %d", imapcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Get the challenge message */
-    imap_get_message(data->state.buffer, &chlgmsg);
-
-    /* Decode the security challenge and create the response message */
-    result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
-                                                      &conn->krb5,
-                                                      &respmsg, &len);
-    if(result) {
-      if(result == CURLE_BAD_CONTENT_ENCODING) {
-        /* Send the cancellation */
-        result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
-
-        if(!result)
-          state(conn, IMAP_AUTHENTICATE_CANCEL);
-      }
-    }
-    else {
-      /* Send the response */
-      if(respmsg) {
-        result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", respmsg);
-
-        if(!result)
-          state(conn, IMAP_AUTHENTICATE_FINAL);
+  result = Curl_sasl_continue(&imapc->sasl, conn, imapcode, &progress);
+  if(!result)
+    switch(progress) {
+    case SASL_DONE:
+      state(conn, IMAP_STOP);  /* Authenticated */
+      break;
+    case SASL_IDLE:            /* No mechanism left after cancellation */
+      if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
+        /* Perform clear text authentication */
+        result = imap_perform_login(conn);
+      else {
+        failf(data, "Authentication cancelled");
+        result = CURLE_LOGIN_DENIED;
       }
+      break;
+    default:
+      break;
     }
-  }
-
-  Curl_safefree(respmsg);
-
-  return result;
-}
-#endif
-
-/* For AUTHENTICATE XOAUTH2 (without initial response) responses */
-static CURLcode imap_state_auth_xoauth2_resp(struct connectdata *conn,
-                                             int imapcode,
-                                             imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *xoauth = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != '+') {
-    failf(data, "Access denied: %d", imapcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the authorisation message */
-    result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
-                                              conn->xoauth2_bearer,
-                                              &xoauth, &len);
-    if(!result && xoauth) {
-      /* Send the message */
-      result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", xoauth);
-
-      if(!result)
-        state(conn, IMAP_AUTHENTICATE_FINAL);
-    }
-  }
-
-  Curl_safefree(xoauth);
-
-  return result;
-}
-
-/* For AUTHENTICATE cancellation responses */
-static CURLcode imap_state_auth_cancel_resp(struct connectdata *conn,
-                                            int imapcode,
-                                            imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct imap_conn *imapc = &conn->proto.imapc;
-  const char *mech = NULL;
-  char *initresp = NULL;
-  size_t len = 0;
-  imapstate state1 = IMAP_STOP;
-  imapstate state2 = IMAP_STOP;
-
-  (void)imapcode;
-  (void)instate; /* no use for this yet */
-
-  /* Remove the offending mechanism from the supported list */
-  imapc->sasl.authmechs ^= imapc->sasl.authused;
-
-  /* Calculate alternative SASL login details */
-  result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
-                                  &state2);
-
-  if(!result) {
-    /* Do we have any mechanisms left or can we fallback to clear text? */
-    if(mech) {
-      /* Retry SASL based authentication */
-      result = imap_perform_authenticate(conn, mech, initresp, state1, state2);
-
-      Curl_safefree(initresp);
-    }
-    else if((!imapc->login_disabled) &&
-            (imapc->preftype & IMAP_TYPE_CLEARTEXT))
-      /* Perform clear text authentication */
-      result = imap_perform_login(conn);
-    else {
-      failf(data, "Authentication cancelled");
-
-      result = CURLE_LOGIN_DENIED;
-    }
-  }
-
-  return result;
-}
-
-/* For final responses in the AUTHENTICATE sequence */
-static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
-                                           int imapcode,
-                                           imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != 'O') {
-    failf(data, "Authentication failed: %d", imapcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else
-    /* End of connect phase */
-    state(conn, IMAP_STOP);
 
   return result;
 }
@@ -1862,69 +1317,8 @@ static CURLcode imap_statemach_act(struct connectdata *conn)
       result = imap_state_starttls_resp(conn, imapcode, imapc->state);
       break;
 
-    case IMAP_AUTHENTICATE_PLAIN:
-      result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_AUTHENTICATE_LOGIN:
-      result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_AUTHENTICATE_LOGIN_PASSWD:
-      result = imap_state_auth_login_password_resp(conn, imapcode,
-                                                   imapc->state);
-      break;
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-    case IMAP_AUTHENTICATE_CRAMMD5:
-      result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_AUTHENTICATE_DIGESTMD5:
-      result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
-      result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
-      break;
-#endif
-
-#ifdef USE_NTLM
-    case IMAP_AUTHENTICATE_NTLM:
-      result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
-      result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
-                                                  imapc->state);
-      break;
-#endif
-
-#if defined(USE_KERBEROS5)
-    case IMAP_AUTHENTICATE_GSSAPI:
-      result = imap_state_auth_gssapi_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_AUTHENTICATE_GSSAPI_TOKEN:
-      result = imap_state_auth_gssapi_token_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_AUTHENTICATE_GSSAPI_NO_DATA:
-      result = imap_state_auth_gssapi_no_data_resp(conn, imapcode,
-                                                   imapc->state);
-      break;
-#endif
-
-    case IMAP_AUTHENTICATE_XOAUTH2:
-      result = imap_state_auth_xoauth2_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_AUTHENTICATE_CANCEL:
-      result = imap_state_auth_cancel_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_AUTHENTICATE_FINAL:
-      result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
+    case IMAP_AUTHENTICATE:
+      result = imap_state_auth_resp(conn, imapcode, imapc->state);
       break;
 
     case IMAP_LOGIN:
@@ -2051,7 +1445,7 @@ static CURLcode imap_connect(struct connectdata *conn, bool *done)
 
   /* Set the default preferred authentication type and mechanism */
   imapc->preftype = IMAP_TYPE_ANY;
-  Curl_sasl_init(&imapc->sasl);
+  Curl_sasl_init(&imapc->sasl, &saslimap);
 
   /* Initialise the pingpong layer */
   Curl_pp_init(pp);
@@ -2748,108 +2142,4 @@ static CURLcode imap_parse_custom_request(struct connectdata *conn)
   return result;
 }
 
-/***********************************************************************
- *
- * imap_calc_sasl_details()
- *
- * Calculate the required login details for SASL authentication.
- */
-static CURLcode imap_calc_sasl_details(struct connectdata *conn,
-                                       const char **mech,
-                                       char **initresp, size_t *len,
-                                       imapstate *state1, imapstate *state2)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct imap_conn *imapc = &conn->proto.imapc;
-
-  /* Calculate the supported authentication mechanism, by decreasing order of
-     security, as well as the initial response where appropriate */
-#if defined(USE_KERBEROS5)
-    if((imapc->sasl.authmechs & SASL_MECH_GSSAPI) &&
-       (imapc->sasl.prefmech & SASL_MECH_GSSAPI)) {
-    imapc->sasl.mutual_auth = FALSE; /* TODO: Calculate mutual auth. */
-
-    *mech = SASL_MECH_STRING_GSSAPI;
-    *state1 = IMAP_AUTHENTICATE_GSSAPI;
-    *state2 = IMAP_AUTHENTICATE_GSSAPI_TOKEN;
-    imapc->sasl.authused = SASL_MECH_GSSAPI;
-
-    if(imapc->ir_supported || data->set.sasl_ir)
-      result = Curl_sasl_create_gssapi_user_message(data, conn->user,
-                                                    conn->passwd, "imap",
-                                                    imapc->sasl.mutual_auth,
-                                                    NULL, &conn->krb5,
-                                                    initresp, len);
-  }
-  else
-#endif
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-  if((imapc->sasl.authmechs & SASL_MECH_DIGEST_MD5) &&
-     (imapc->sasl.prefmech & SASL_MECH_DIGEST_MD5)) {
-    *mech = SASL_MECH_STRING_DIGEST_MD5;
-    *state1 = IMAP_AUTHENTICATE_DIGESTMD5;
-    imapc->sasl.authused = SASL_MECH_DIGEST_MD5;
-  }
-  else if((imapc->sasl.authmechs & SASL_MECH_CRAM_MD5) &&
-          (imapc->sasl.prefmech & SASL_MECH_CRAM_MD5)) {
-    *mech = SASL_MECH_STRING_CRAM_MD5;
-    *state1 = IMAP_AUTHENTICATE_CRAMMD5;
-    imapc->sasl.authused = SASL_MECH_CRAM_MD5;
-  }
-  else
-#endif
-#ifdef USE_NTLM
-    if((imapc->sasl.authmechs & SASL_MECH_NTLM) &&
-       (imapc->sasl.prefmech & SASL_MECH_NTLM)) {
-    *mech = SASL_MECH_STRING_NTLM;
-    *state1 = IMAP_AUTHENTICATE_NTLM;
-    *state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
-    imapc->sasl.authused = SASL_MECH_NTLM;
-
-    if(imapc->ir_supported || data->set.sasl_ir)
-      result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
-                                                   &conn->ntlm,
-                                                   initresp, len);
-  }
-  else
-#endif
-  if(((imapc->sasl.authmechs & SASL_MECH_XOAUTH2) &&
-      (imapc->sasl.prefmech & SASL_MECH_XOAUTH2) &&
-      (imapc->sasl.prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
-    *mech = SASL_MECH_STRING_XOAUTH2;
-    *state1 = IMAP_AUTHENTICATE_XOAUTH2;
-    *state2 = IMAP_AUTHENTICATE_FINAL;
-    imapc->sasl.authused = SASL_MECH_XOAUTH2;
-
-    if(imapc->ir_supported || data->set.sasl_ir)
-      result = Curl_sasl_create_xoauth2_message(data, conn->user,
-                                                conn->xoauth2_bearer,
-                                                initresp, len);
-  }
-  else if((imapc->sasl.authmechs & SASL_MECH_LOGIN) &&
-          (imapc->sasl.prefmech & SASL_MECH_LOGIN)) {
-    *mech = SASL_MECH_STRING_LOGIN;
-    *state1 = IMAP_AUTHENTICATE_LOGIN;
-    *state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
-    imapc->sasl.authused = SASL_MECH_LOGIN;
-
-    if(imapc->ir_supported || data->set.sasl_ir)
-      result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
-  }
-  else if((imapc->sasl.authmechs & SASL_MECH_PLAIN) &&
-          (imapc->sasl.prefmech & SASL_MECH_PLAIN)) {
-    *mech = SASL_MECH_STRING_PLAIN;
-    *state1 = IMAP_AUTHENTICATE_PLAIN;
-    *state2 = IMAP_AUTHENTICATE_FINAL;
-    imapc->sasl.authused = SASL_MECH_PLAIN;
-
-    if(imapc->ir_supported || data->set.sasl_ir)
-      result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
-                                              initresp, len);
-  }
-
-  return result;
-}
-
 #endif /* CURL_DISABLE_IMAP */
index 22e6ada1c6e32ed61b91c91b7a28b883be3f3a0a..3189daa3cfd84957ebe667e8a5174825291762b1 100644 (file)
@@ -36,20 +36,7 @@ typedef enum {
   IMAP_STARTTLS,
   IMAP_UPGRADETLS,   /* asynchronously upgrade the connection to SSL/TLS
                        (multi mode only) */
-  IMAP_AUTHENTICATE_PLAIN,
-  IMAP_AUTHENTICATE_LOGIN,
-  IMAP_AUTHENTICATE_LOGIN_PASSWD,
-  IMAP_AUTHENTICATE_CRAMMD5,
-  IMAP_AUTHENTICATE_DIGESTMD5,
-  IMAP_AUTHENTICATE_DIGESTMD5_RESP,
-  IMAP_AUTHENTICATE_NTLM,
-  IMAP_AUTHENTICATE_NTLM_TYPE2MSG,
-  IMAP_AUTHENTICATE_GSSAPI,
-  IMAP_AUTHENTICATE_GSSAPI_TOKEN,
-  IMAP_AUTHENTICATE_GSSAPI_NO_DATA,
-  IMAP_AUTHENTICATE_XOAUTH2,
-  IMAP_AUTHENTICATE_CANCEL,
-  IMAP_AUTHENTICATE_FINAL,
+  IMAP_AUTHENTICATE,
   IMAP_LOGIN,
   IMAP_LIST,
   IMAP_SELECT,
index f5828e515f32eacb5d9bf327d258bec0bdfd58fd..a7213a44edd343d1350e1811ef64a869be607ba6 100644 (file)
@@ -106,10 +106,10 @@ static CURLcode pop3_setup_connection(struct connectdata *conn);
 static CURLcode pop3_parse_url_options(struct connectdata *conn);
 static CURLcode pop3_parse_url_path(struct connectdata *conn);
 static CURLcode pop3_parse_custom_request(struct connectdata *conn);
-static CURLcode pop3_calc_sasl_details(struct connectdata *conn,
-                                       const char **mech,
-                                       char **initresp, size_t *len,
-                                       pop3state *state1, pop3state *state2);
+static CURLcode pop3_perform_auth(struct connectdata *conn, const char *mech,
+                                  const char *initresp);
+static CURLcode pop3_continue_auth(struct connectdata *conn, const char *resp);
+static void pop3_get_message(char *buffer, char** outptr);
 
 /*
  * POP3 protocol handler.
@@ -214,6 +214,17 @@ static const struct Curl_handler Curl_handler_pop3s_proxy = {
 #endif
 #endif
 
+/* SASL parameters for the pop3 protocol */
+static const struct SASLproto saslpop3 = {
+  "pop",                      /* The service name */
+  '+',                        /* Code received when continuation is expected */
+  '+',                        /* Code to receive upon authentication success */
+  255 - 8,                    /* Maximum initial response length (no max) */
+  pop3_perform_auth,          /* Send authentication command */
+  pop3_continue_auth,         /* Send authentication continuation */
+  pop3_get_message            /* Get SASL response message */
+};
+
 #ifdef USE_SSL
 static void pop3_to_pop3s(struct connectdata *conn)
 {
@@ -312,20 +323,7 @@ static void state(struct connectdata *conn, pop3state newstate)
     "CAPA",
     "STARTTLS",
     "UPGRADETLS",
-    "AUTH_PLAIN",
-    "AUTH_LOGIN",
-    "AUTH_LOGIN_PASSWD",
-    "AUTH_CRAMMD5",
-    "AUTH_DIGESTMD5",
-    "AUTH_DIGESTMD5_RESP",
-    "AUTH_NTLM",
-    "AUTH_NTLM_TYPE2MSG",
-    "AUTH_GSSAPI",
-    "AUTH_GSSAPI_TOKEN",
-    "AUTH_GSSAPI_NO_DATA",
-    "AUTH_XOAUTH2",
-    "AUTH_CANCEL",
-    "AUTH_FINAL",
+    "AUTH",
     "APOP",
     "USER",
     "PASS",
@@ -500,30 +498,37 @@ static CURLcode pop3_perform_apop(struct connectdata *conn)
  */
 static CURLcode pop3_perform_auth(struct connectdata *conn,
                                   const char *mech,
-                                  const char *initresp, size_t len,
-                                  pop3state state1, pop3state state2)
+                                  const char *initresp)
 {
   CURLcode result = CURLE_OK;
   struct pop3_conn *pop3c = &conn->proto.pop3c;
 
-  if(initresp && 8 + strlen(mech) + len <= 255) { /* AUTH <mech> ...<crlf> */
+  if(initresp) {                                  /* AUTH <mech> ...<crlf> */
     /* Send the AUTH command with the initial response */
     result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp);
-
-    if(!result)
-      state(conn, state2);
   }
   else {
     /* Send the AUTH command */
     result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
-
-    if(!result)
-      state(conn, state1);
   }
 
   return result;
 }
 
+/***********************************************************************
+ *
+ * pop3_continue_auth()
+ *
+ * Sends SASL continuation data or cancellation.
+ */
+static CURLcode pop3_continue_auth(struct connectdata *conn,
+                                   const char *resp)
+{
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+
+  return Curl_pp_sendf(&pop3c->pp, "%s", resp);
+}
+
 /***********************************************************************
  *
  * pop3_perform_authentication()
@@ -536,38 +541,32 @@ static CURLcode pop3_perform_authentication(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
   struct pop3_conn *pop3c = &conn->proto.pop3c;
-  const char *mech = NULL;
-  char *initresp = NULL;
-  size_t len = 0;
-  pop3state state1 = POP3_STOP;
-  pop3state state2 = POP3_STOP;
+  saslprogress progress = SASL_IDLE;
 
   /* Check we have a username and password to authenticate with and end the
      connect phase if we don't */
   if(!conn->bits.user_passwd) {
     state(conn, POP3_STOP);
-
     return result;
   }
 
-  /* Calculate the SASL login details */
-  if(pop3c->authtypes & POP3_TYPE_SASL)
-    result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
-                                    &state2);
+  if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) {
+    /* Calculate the SASL login details */
+    result = Curl_sasl_start(&pop3c->sasl, conn, FALSE, &progress);
 
-  if(!result) {
-    if(mech && (pop3c->preftype & POP3_TYPE_SASL)) {
-      /* Perform SASL based authentication */
-      result = pop3_perform_auth(conn, mech, initresp, len, state1, state2);
-    }
+    if(!result)
+      if(progress == SASL_INPROGRESS)
+        state(conn, POP3_AUTH);
+  }
+
+  if(!result && progress == SASL_IDLE) {
 #ifndef CURL_DISABLE_CRYPTO_AUTH
-    else if((pop3c->authtypes & POP3_TYPE_APOP) &&
-            (pop3c->preftype & POP3_TYPE_APOP))
+    if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
       /* Perform APOP authentication */
       result = pop3_perform_apop(conn);
+    else
 #endif
-    else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) &&
-            (pop3c->preftype & POP3_TYPE_CLEARTEXT))
+    if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
       /* Perform clear text authentication */
       result = pop3_perform_user(conn);
     else {
@@ -577,8 +576,6 @@ static CURLcode pop3_perform_authentication(struct connectdata *conn)
     }
   }
 
-  Curl_safefree(initresp);
-
   return result;
 }
 
@@ -807,575 +804,42 @@ static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
   return result;
 }
 
-/* For AUTH PLAIN (without initial response) responses */
-static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn,
-                                           int pop3code,
-                                           pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *plainauth = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied. %c", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the authorisation message */
-    result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
-                                            &plainauth, &len);
-    if(!result && plainauth) {
-      /* Send the message */
-      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth);
-
-      if(!result)
-        state(conn, POP3_AUTH_FINAL);
-    }
-  }
-
-  Curl_safefree(plainauth);
-
-  return result;
-}
-
-/* For AUTH LOGIN (without initial response) responses */
-static CURLcode pop3_state_auth_login_resp(struct connectdata *conn,
-                                           int pop3code,
-                                           pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *authuser = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the user message */
-    result = Curl_sasl_create_login_message(data, conn->user,
-                                            &authuser, &len);
-    if(!result && authuser) {
-      /* Send the user */
-      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser);
-
-      if(!result)
-        state(conn, POP3_AUTH_LOGIN_PASSWD);
-    }
-  }
-
-  Curl_safefree(authuser);
-
-  return result;
-}
-
-/* For AUTH LOGIN user entry responses */
-static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn,
-                                                    int pop3code,
-                                                    pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *authpasswd = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the password message */
-    result = Curl_sasl_create_login_message(data, conn->passwd,
-                                            &authpasswd, &len);
-    if(!result && authpasswd) {
-      /* Send the password */
-      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd);
-
-      if(!result)
-        state(conn, POP3_AUTH_FINAL);
-    }
-  }
-
-  Curl_safefree(authpasswd);
-
-  return result;
-}
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-/* For AUTH CRAM-MD5 responses */
-static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn,
-                                          int pop3code,
-                                          pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlg = NULL;
-  char *chlg64 = NULL;
-  char *rplyb64 = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    return CURLE_LOGIN_DENIED;
-  }
-
-  /* Get the challenge message */
-  pop3_get_message(data->state.buffer, &chlg64);
-
-  /* Decode the challenge message */
-  result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
-  if(result) {
-    /* Send the cancellation */
-    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
-
-    if(!result)
-      state(conn, POP3_AUTH_CANCEL);
-  }
-  else {
-    /* Create the response message */
-    result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
-                                               conn->passwd, &rplyb64, &len);
-    if(!result && rplyb64) {
-      /* Send the response */
-      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
-
-      if(!result)
-        state(conn, POP3_AUTH_FINAL);
-    }
-  }
-
-  Curl_safefree(chlg);
-  Curl_safefree(rplyb64);
-
-  return result;
-}
-
-/* For AUTH DIGEST-MD5 challenge responses */
-static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn,
-                                            int pop3code,
-                                            pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlg64 = NULL;
-  char *rplyb64 = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    return CURLE_LOGIN_DENIED;
-  }
-
-  /* Get the challenge message */
-  pop3_get_message(data->state.buffer, &chlg64);
-
-  /* Create the response message */
-  result = Curl_sasl_create_digest_md5_message(data, chlg64,
-                                               conn->user, conn->passwd,
-                                               "pop", &rplyb64, &len);
-  if(result) {
-    if(result == CURLE_BAD_CONTENT_ENCODING) {
-      /* Send the cancellation */
-      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
-
-      if(!result)
-        state(conn, POP3_AUTH_CANCEL);
-    }
-  }
-  else {
-    /* Send the response */
-    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
-
-    if(!result)
-      state(conn, POP3_AUTH_DIGESTMD5_RESP);
-  }
-
-  Curl_safefree(rplyb64);
-
-  return result;
-}
-
-/* For AUTH DIGEST-MD5 challenge-response responses */
-static CURLcode pop3_state_auth_digest_resp_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;
-  }
-  else {
-    /* Send an empty response */
-    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "");
-
-    if(!result)
-      state(conn, POP3_AUTH_FINAL);
-  }
-
-  return result;
-}
-#endif
-
-#ifdef USE_NTLM
-/* For AUTH NTLM (without initial response) responses */
-static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
-                                          int pop3code,
-                                          pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *type1msg = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the type-1 message */
-    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
-                                                 &conn->ntlm,
-                                                 &type1msg, &len);
-    if(!result && type1msg) {
-      /* Send the message */
-      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);
-
-      if(!result)
-        state(conn, POP3_AUTH_NTLM_TYPE2MSG);
-    }
-  }
-
-  Curl_safefree(type1msg);
-
-  return result;
-}
-
-/* For NTLM type-2 responses (sent in reponse to our type-1 message) */
-static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
-                                                   int pop3code,
-                                                   pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *type2msg = NULL;
-  char *type3msg = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Get the type-2 message */
-    pop3_get_message(data->state.buffer, &type2msg);
-
-    /* Decode the type-2 message */
-    result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
-    if(result) {
-      /* Send the cancellation */
-      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
-
-      if(!result)
-        state(conn, POP3_AUTH_CANCEL);
-    }
-    else {
-      /* Create the type-3 message */
-      result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
-                                                   conn->passwd, &conn->ntlm,
-                                                   &type3msg, &len);
-      if(!result && type3msg) {
-        /* Send the message */
-        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);
-
-        if(!result)
-          state(conn, POP3_AUTH_FINAL);
-      }
-    }
-  }
-
-  Curl_safefree(type3msg);
-
-  return result;
-}
-#endif
-
-#if defined(USE_KERBEROS5)
-/* For AUTH GSSAPI (without initial response) responses */
-static CURLcode pop3_state_auth_gssapi_resp(struct connectdata *conn,
-                                            int pop3code,
-                                            pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  size_t len = 0;
-  char *respmsg = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the initial response message */
-    result = Curl_sasl_create_gssapi_user_message(data, conn->user,
-                                                  conn->passwd, "pop",
-                                                  pop3c->sasl.mutual_auth,
-                                                  NULL, &conn->krb5,
-                                                  &respmsg, &len);
-    if(!result && respmsg) {
-      /* Send the message */
-      result = Curl_pp_sendf(&pop3c->pp, "%s", respmsg);
-
-      if(!result)
-        state(conn, POP3_AUTH_GSSAPI_TOKEN);
-    }
-  }
-
-  Curl_safefree(respmsg);
-
-  return result;
-}
-
-/* For AUTH GSSAPI user token responses */
-static CURLcode pop3_state_auth_gssapi_token_resp(struct connectdata *conn,
-                                                  int pop3code,
-                                                  pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  char *chlgmsg = NULL;
-  char *respmsg = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Get the challenge message */
-    pop3_get_message(data->state.buffer, &chlgmsg);
-
-    if(pop3c->sasl.mutual_auth)
-      /* Decode the user token challenge and create the optional response
-         message */
-      result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL,
-                                                    pop3c->sasl.mutual_auth,
-                                                    chlgmsg, &conn->krb5,
-                                                    &respmsg, &len);
-    else
-      /* Decode the security challenge and create the response message */
-      result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
-                                                        &conn->krb5,
-                                                        &respmsg, &len);
-
-    if(result) {
-      if(result == CURLE_BAD_CONTENT_ENCODING) {
-        /* Send the cancellation */
-        result = Curl_pp_sendf(&pop3c->pp, "%s", "*");
-
-        if(!result)
-          state(conn, POP3_AUTH_CANCEL);
-      }
-    }
-    else {
-      /* Send the response */
-      if(respmsg)
-        result = Curl_pp_sendf(&pop3c->pp, "%s", respmsg);
-      else
-        result = Curl_pp_sendf(&pop3c->pp, "%s", "");
-
-      if(!result)
-        state(conn, (pop3c->sasl.mutual_auth ? POP3_AUTH_GSSAPI_NO_DATA :
-                                               POP3_AUTH_FINAL));
-    }
-  }
-
-  Curl_safefree(respmsg);
-
-  return result;
-}
-
-/* For AUTH GSSAPI no data responses */
-static CURLcode pop3_state_auth_gssapi_no_data_resp(struct connectdata *conn,
-                                                    int pop3code,
-                                                    pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlgmsg = NULL;
-  char *respmsg = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Get the challenge message */
-    pop3_get_message(data->state.buffer, &chlgmsg);
-
-    /* Decode the security challenge and create the security message */
-    result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
-                                                      &conn->krb5,
-                                                      &respmsg, &len);
-    if(result) {
-      if(result == CURLE_BAD_CONTENT_ENCODING) {
-        /* Send the cancellation */
-        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
-
-        if(!result)
-          state(conn, POP3_AUTH_CANCEL);
-      }
-    }
-    else {
-      /* Send the response */
-      if(respmsg) {
-        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", respmsg);
-
-        if(!result)
-          state(conn, POP3_AUTH_FINAL);
-      }
-    }
-  }
-
-  Curl_safefree(respmsg);
-
-  return result;
-}
-#endif
-
-/* For AUTH XOAUTH2 (without initial response) responses */
-static CURLcode pop3_state_auth_xoauth2_resp(struct connectdata *conn,
-                                             int pop3code, pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *xoauth = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the authorisation message */
-    result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
-                                              conn->xoauth2_bearer,
-                                              &xoauth, &len);
-    if(!result && xoauth) {
-      /* Send the message */
-      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", xoauth);
-
-      if(!result)
-        state(conn, POP3_AUTH_FINAL);
-    }
-  }
-
-  Curl_safefree(xoauth);
-
-  return result;
-}
-
-/* For AUTH cancellation responses */
-static CURLcode pop3_state_auth_cancel_resp(struct connectdata *conn,
-                                            int pop3code,
-                                            pop3state instate)
+/* For SASL authentication responses */
+static CURLcode pop3_state_auth_resp(struct connectdata *conn,
+                                     int pop3code,
+                                     pop3state instate)
 {
   CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
   struct pop3_conn *pop3c = &conn->proto.pop3c;
-  const char *mech = NULL;
-  char *initresp = NULL;
-  size_t len = 0;
-  pop3state state1 = POP3_STOP;
-  pop3state state2 = POP3_STOP;
+  saslprogress progress;
 
-  (void)pop3code;
   (void)instate; /* no use for this yet */
 
-  /* Remove the offending mechanism from the supported list */
-  pop3c->sasl.authmechs ^= pop3c->sasl.authused;
-
-  /* Calculate alternative SASL login details */
-  result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
-                                  &state2);
-
-  if(!result) {
-    /* Do we have any mechanisms left or can we fallback to another
-       authentication type? */
-    if(mech) {
-      /* Retry SASL based authentication */
-      result = pop3_perform_auth(conn, mech, initresp, len, state1, state2);
-
-      Curl_safefree(initresp);
-    }
+  result = Curl_sasl_continue(&pop3c->sasl, conn, pop3code, &progress);
+  if(!result)
+    switch(progress) {
+    case SASL_DONE:
+      state(conn, POP3_STOP);  /* Authenticated */
+      break;
+    case SASL_IDLE:            /* No mechanism left after cancellation */
 #ifndef CURL_DISABLE_CRYPTO_AUTH
-    else if((pop3c->authtypes & POP3_TYPE_APOP) &&
-            (pop3c->preftype & POP3_TYPE_APOP))
-      /* Perform APOP authentication */
-      result = pop3_perform_apop(conn);
+      if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
+        /* Perform APOP authentication */
+        result = pop3_perform_apop(conn);
+      else
 #endif
-    else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) &&
-            (pop3c->preftype & POP3_TYPE_CLEARTEXT))
-      /* Perform clear text authentication */
-      result = pop3_perform_user(conn);
-    else {
-      failf(data, "Authentication cancelled");
-
-      result = CURLE_LOGIN_DENIED;
+      if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
+        /* Perform clear text authentication */
+        result = pop3_perform_user(conn);
+      else {
+        failf(data, "Authentication cancelled");
+        result = CURLE_LOGIN_DENIED;
+      }
+      break;
+    default:
+      break;
     }
-  }
-
-  return result;
-}
-
-/* For final responses in the AUTH sequence */
-static CURLcode pop3_state_auth_final_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;
-  }
-  else
-    /* End of connect phase */
-    state(conn, POP3_STOP);
 
   return result;
 }
@@ -1542,69 +1006,8 @@ static CURLcode pop3_statemach_act(struct connectdata *conn)
       result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
       break;
 
-    case POP3_AUTH_PLAIN:
-      result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_LOGIN:
-      result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_LOGIN_PASSWD:
-      result = pop3_state_auth_login_password_resp(conn, pop3code,
-                                                   pop3c->state);
-      break;
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-    case POP3_AUTH_CRAMMD5:
-      result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_DIGESTMD5:
-      result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_DIGESTMD5_RESP:
-      result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
-      break;
-#endif
-
-#ifdef USE_NTLM
-    case POP3_AUTH_NTLM:
-      result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_NTLM_TYPE2MSG:
-      result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
-                                                  pop3c->state);
-      break;
-#endif
-
-#if defined(USE_KERBEROS5)
-    case POP3_AUTH_GSSAPI:
-      result = pop3_state_auth_gssapi_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_GSSAPI_TOKEN:
-      result = pop3_state_auth_gssapi_token_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_GSSAPI_NO_DATA:
-      result = pop3_state_auth_gssapi_no_data_resp(conn, pop3code,
-                                                   pop3c->state);
-      break;
-#endif
-
-    case POP3_AUTH_XOAUTH2:
-      result = pop3_state_auth_xoauth2_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_CANCEL:
-      result = pop3_state_auth_cancel_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_FINAL:
-      result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
+    case POP3_AUTH:
+      result = pop3_state_auth_resp(conn, pop3code, pop3c->state);
       break;
 
 #ifndef CURL_DISABLE_CRYPTO_AUTH
@@ -1717,7 +1120,7 @@ static CURLcode pop3_connect(struct connectdata *conn, bool *done)
 
   /* Set the default preferred authentication type and mechanism */
   pop3c->preftype = POP3_TYPE_ANY;
-  Curl_sasl_init(&pop3c->sasl);
+  Curl_sasl_init(&pop3c->sasl, &saslpop3);
 
   /* Initialise the pingpong layer */
   Curl_pp_init(pp);
@@ -2070,110 +1473,6 @@ static CURLcode pop3_parse_custom_request(struct connectdata *conn)
   return result;
 }
 
-/***********************************************************************
- *
- * pop3_calc_sasl_details()
- *
- * Calculate the required login details for SASL authentication.
- */
-static CURLcode pop3_calc_sasl_details(struct connectdata *conn,
-                                       const char **mech,
-                                       char **initresp, size_t *len,
-                                       pop3state *state1, pop3state *state2)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-
-  /* Calculate the supported authentication mechanism, by decreasing order of
-     security, as well as the initial response where appropriate */
-#if defined(USE_KERBEROS5)
-  if((pop3c->sasl.authmechs & SASL_MECH_GSSAPI) &&
-      (pop3c->sasl.prefmech & SASL_MECH_GSSAPI)) {
-    pop3c->sasl.mutual_auth = FALSE; /* TODO: Calculate mutual auth. */
-
-    *mech = SASL_MECH_STRING_GSSAPI;
-    *state1 = POP3_AUTH_GSSAPI;
-    *state2 = POP3_AUTH_GSSAPI_TOKEN;
-    pop3c->sasl.authused = SASL_MECH_GSSAPI;
-
-    if(data->set.sasl_ir)
-      result = Curl_sasl_create_gssapi_user_message(data, conn->user,
-                                                    conn->passwd, "pop",
-                                                    pop3c->sasl.mutual_auth,
-                                                    NULL, &conn->krb5,
-                                                    initresp, len);
-  }
-  else
-#endif
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-  if((pop3c->sasl.authmechs & SASL_MECH_DIGEST_MD5) &&
-      (pop3c->sasl.prefmech & SASL_MECH_DIGEST_MD5)) {
-    *mech = SASL_MECH_STRING_DIGEST_MD5;
-    *state1 = POP3_AUTH_DIGESTMD5;
-    pop3c->sasl.authused = SASL_MECH_DIGEST_MD5;
-  }
-  else if((pop3c->sasl.authmechs & SASL_MECH_CRAM_MD5) &&
-          (pop3c->sasl.prefmech & SASL_MECH_CRAM_MD5)) {
-    *mech = SASL_MECH_STRING_CRAM_MD5;
-    *state1 = POP3_AUTH_CRAMMD5;
-    pop3c->sasl.authused = SASL_MECH_CRAM_MD5;
-  }
-  else
-#endif
-#ifdef USE_NTLM
-  if((pop3c->sasl.authmechs & SASL_MECH_NTLM) &&
-      (pop3c->sasl.prefmech & SASL_MECH_NTLM)) {
-    *mech = SASL_MECH_STRING_NTLM;
-    *state1 = POP3_AUTH_NTLM;
-    *state2 = POP3_AUTH_NTLM_TYPE2MSG;
-    pop3c->sasl.authused = SASL_MECH_NTLM;
-
-    if(data->set.sasl_ir)
-      result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
-                                                    &conn->ntlm,
-                                                    initresp, len);
-  }
-  else
-#endif
-  if(((pop3c->sasl.authmechs & SASL_MECH_XOAUTH2) &&
-      (pop3c->sasl.prefmech & SASL_MECH_XOAUTH2) &&
-      (pop3c->sasl.prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
-    *mech = SASL_MECH_STRING_XOAUTH2;
-    *state1 = POP3_AUTH_XOAUTH2;
-    *state2 = POP3_AUTH_FINAL;
-    pop3c->sasl.authused = SASL_MECH_XOAUTH2;
-
-    if(data->set.sasl_ir)
-      result = Curl_sasl_create_xoauth2_message(data, conn->user,
-                                                conn->xoauth2_bearer,
-                                                initresp, len);
-  }
-  else if((pop3c->sasl.authmechs & SASL_MECH_LOGIN) &&
-          (pop3c->sasl.prefmech & SASL_MECH_LOGIN)) {
-    *mech = SASL_MECH_STRING_LOGIN;
-    *state1 = POP3_AUTH_LOGIN;
-    *state2 = POP3_AUTH_LOGIN_PASSWD;
-    pop3c->sasl.authused = SASL_MECH_LOGIN;
-
-    if(data->set.sasl_ir)
-      result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
-  }
-  else if((pop3c->sasl.authmechs & SASL_MECH_PLAIN) &&
-          (pop3c->sasl.prefmech & SASL_MECH_PLAIN)) {
-    *mech = SASL_MECH_STRING_PLAIN;
-    *state1 = POP3_AUTH_PLAIN;
-    *state2 = POP3_AUTH_FINAL;
-    pop3c->sasl.authused = SASL_MECH_PLAIN;
-
-    if(data->set.sasl_ir)
-      result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
-                                              initresp, len);
-  }
-
-  return result;
-}
-
 /***********************************************************************
  *
  * Curl_pop3_write()
index 0cb5852d89b7f0a184a5628909fbc64785db6ae0..7bc53aaf5ad3c2868e304f25937e871ac3c4b35e 100644 (file)
@@ -36,20 +36,7 @@ typedef enum {
   POP3_STARTTLS,
   POP3_UPGRADETLS,   /* asynchronously upgrade the connection to SSL/TLS
                        (multi mode only) */
-  POP3_AUTH_PLAIN,
-  POP3_AUTH_LOGIN,
-  POP3_AUTH_LOGIN_PASSWD,
-  POP3_AUTH_CRAMMD5,
-  POP3_AUTH_DIGESTMD5,
-  POP3_AUTH_DIGESTMD5_RESP,
-  POP3_AUTH_NTLM,
-  POP3_AUTH_NTLM_TYPE2MSG,
-  POP3_AUTH_GSSAPI,
-  POP3_AUTH_GSSAPI_TOKEN,
-  POP3_AUTH_GSSAPI_NO_DATA,
-  POP3_AUTH_XOAUTH2,
-  POP3_AUTH_CANCEL,
-  POP3_AUTH_FINAL,
+  POP3_AUTH,
   POP3_APOP,
   POP3_USER,
   POP3_PASS,
index e5ddedb02dc1f47c6450747df9e9ec8172cbe1ed..ca1ab2cece66e28994a86608693badc8b1f1d65b 100644 (file)
@@ -105,10 +105,10 @@ static CURLcode smtp_setup_connection(struct connectdata *conn);
 static CURLcode smtp_parse_url_options(struct connectdata *conn);
 static CURLcode smtp_parse_url_path(struct connectdata *conn);
 static CURLcode smtp_parse_custom_request(struct connectdata *conn);
-static CURLcode smtp_calc_sasl_details(struct connectdata *conn,
-                                       const char **mech,
-                                       char **initresp, size_t *len,
-                                       smtpstate *state1, smtpstate *state2);
+static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech,
+                                  const char *initresp);
+static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp);
+static void smtp_get_message(char *buffer, char** outptr);
 
 /*
  * SMTP protocol handler.
@@ -213,6 +213,17 @@ static const struct Curl_handler Curl_handler_smtps_proxy = {
 #endif
 #endif
 
+/* SASL parameters for the smtp protocol */
+static const struct SASLproto saslsmtp = {
+  "smtp",                     /* The service name */
+  334,                        /* Code received when continuation is expected */
+  235,                        /* Code to receive upon authentication success */
+  512 - 8,                    /* Maximum initial response length (no max) */
+  smtp_perform_auth,          /* Send authentication command */
+  smtp_continue_auth,         /* Send authentication continuation */
+  smtp_get_message            /* Get SASL response message */
+};
+
 #ifdef USE_SSL
 static void smtp_to_smtps(struct connectdata *conn)
 {
@@ -309,20 +320,7 @@ static void state(struct connectdata *conn, smtpstate newstate)
     "HELO",
     "STARTTLS",
     "UPGRADETLS",
-    "AUTH_PLAIN",
-    "AUTH_LOGIN",
-    "AUTH_LOGIN_PASSWD",
-    "AUTH_CRAMMD5",
-    "AUTH_DIGESTMD5",
-    "AUTH_DIGESTMD5_RESP",
-    "AUTH_NTLM",
-    "AUTH_NTLM_TYPE2MSG",
-    "AUTH_GSSAPI",
-    "AUTH_GSSAPI_TOKEN",
-    "AUTH_GSSAPI_NO_DATA",
-    "AUTH_XOAUTH2",
-    "AUTH_CANCEL",
-    "AUTH_FINAL",
+    "AUTH",
     "COMMAND",
     "MAIL",
     "RCPT",
@@ -445,30 +443,36 @@ static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
  */
 static CURLcode smtp_perform_auth(struct connectdata *conn,
                                   const char *mech,
-                                  const char *initresp, size_t len,
-                                  smtpstate state1, smtpstate state2)
+                                  const char *initresp)
 {
   CURLcode result = CURLE_OK;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
 
-  if(initresp && 8 + strlen(mech) + len <= 512) { /* AUTH <mech> ...<crlf> */
+  if(initresp) {                                  /* AUTH <mech> ...<crlf> */
     /* Send the AUTH command with the initial response */
     result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
-
-    if(!result)
-      state(conn, state2);
   }
   else {
     /* Send the AUTH command */
     result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
-
-    if(!result)
-      state(conn, state1);
   }
 
   return result;
 }
 
+/***********************************************************************
+ *
+ * smtp_continue_auth()
+ *
+ * Sends SASL continuation data or cancellation.
+ */
+static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp)
+{
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+  return Curl_pp_sendf(&smtpc->pp, "%s", resp);
+}
+
 /***********************************************************************
  *
  * smtp_perform_authentication()
@@ -480,31 +484,21 @@ static CURLcode smtp_perform_authentication(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
-  const char *mech = NULL;
-  char *initresp = NULL;
-  size_t len = 0;
-  smtpstate state1 = SMTP_STOP;
-  smtpstate state2 = SMTP_STOP;
+  saslprogress progress;
 
   /* Check we have a username and password to authenticate with, and the
      server supports authentiation, and end the connect phase if not */
   if(!conn->bits.user_passwd || !smtpc->auth_supported) {
     state(conn, SMTP_STOP);
-
     return result;
   }
 
   /* Calculate the SASL login details */
-  result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
-                                  &state2);
+  result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress);
 
   if(!result) {
-    if(mech) {
-      /* Perform SASL based authentication */
-      result = smtp_perform_auth(conn, mech, initresp, len, state1, state2);
-
-      Curl_safefree(initresp);
-    }
+    if(progress == SASL_INPROGRESS)
+      state(conn, SMTP_AUTH);
     else {
       /* Other mechanisms not supported */
       infof(conn->data, "No known authentication mechanisms supported!\n");
@@ -825,566 +819,31 @@ static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
   return result;
 }
 
-/* For AUTH PLAIN (without initial response) responses */
-static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
-                                           int smtpcode,
-                                           smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *plainauth = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the authorisation message */
-    result = Curl_sasl_create_plain_message(conn->data, conn->user,
-                                            conn->passwd, &plainauth, &len);
-    if(!result && plainauth) {
-      /* Send the message */
-      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
-
-      if(!result)
-        state(conn, SMTP_AUTH_FINAL);
-    }
-  }
-
-  Curl_safefree(plainauth);
-
-  return result;
-}
-
-/* For AUTH LOGIN (without initial response) responses */
-static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
-                                           int smtpcode,
-                                           smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *authuser = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the user message */
-    result = Curl_sasl_create_login_message(conn->data, conn->user,
-                                            &authuser, &len);
-    if(!result && authuser) {
-      /* Send the user */
-      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
-
-      if(!result)
-        state(conn, SMTP_AUTH_LOGIN_PASSWD);
-    }
-  }
-
-  Curl_safefree(authuser);
-
-  return result;
-}
-
-/* For AUTH LOGIN user entry responses */
-static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
-                                                    int smtpcode,
-                                                    smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *authpasswd = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the password message */
-    result = Curl_sasl_create_login_message(conn->data, conn->passwd,
-                                            &authpasswd, &len);
-    if(!result && authpasswd) {
-      /* Send the password */
-      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
-
-      if(!result)
-        state(conn, SMTP_AUTH_FINAL);
-    }
-  }
-
-  Curl_safefree(authpasswd);
-
-  return result;
-}
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-/* For AUTH CRAM-MD5 responses */
-static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
-                                          int smtpcode,
-                                          smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlg = NULL;
-  char *chlg64 = NULL;
-  char *rplyb64 = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    return CURLE_LOGIN_DENIED;
-  }
-
-  /* Get the challenge message */
-  smtp_get_message(data->state.buffer, &chlg64);
-
-  /* Decode the challenge message */
-  result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
-  if(result) {
-    /* Send the cancellation */
-    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
-
-    if(!result)
-      state(conn, SMTP_AUTH_CANCEL);
-  }
-  else {
-    /* Create the response message */
-    result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
-                                               conn->passwd, &rplyb64, &len);
-    if(!result && rplyb64) {
-      /* Send the response */
-      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
-
-      if(!result)
-        state(conn, SMTP_AUTH_FINAL);
-    }
-  }
-
-  Curl_safefree(chlg);
-  Curl_safefree(rplyb64);
-
-  return result;
-}
-
-/* For AUTH DIGEST-MD5 challenge responses */
-static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
-                                            int smtpcode,
-                                            smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlg64 = NULL;
-  char *rplyb64 = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    return CURLE_LOGIN_DENIED;
-  }
-
-  /* Get the challenge message */
-  smtp_get_message(data->state.buffer, &chlg64);
-
-  /* Create the response message */
-  result = Curl_sasl_create_digest_md5_message(data, chlg64,
-                                               conn->user, conn->passwd,
-                                               "smtp", &rplyb64, &len);
-  if(result) {
-    if(result == CURLE_BAD_CONTENT_ENCODING) {
-      /* Send the cancellation */
-      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
-
-      if(!result)
-        state(conn, SMTP_AUTH_CANCEL);
-    }
-  }
-  else {
-    /* Send the response */
-    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
-
-    if(!result)
-      state(conn, SMTP_AUTH_DIGESTMD5_RESP);
-  }
-
-  Curl_safefree(rplyb64);
-
-  return result;
-}
-
-/* For AUTH DIGEST-MD5 challenge-response responses */
-static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
-                                                 int smtpcode,
-                                                 smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Authentication failed: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Send an empty response */
-    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "");
-
-    if(!result)
-      state(conn, SMTP_AUTH_FINAL);
-  }
-
-  return result;
-}
-
-#endif
-
-#ifdef USE_NTLM
-/* For AUTH NTLM (without initial response) responses */
-static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
-                                          int smtpcode,
-                                          smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *type1msg = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the type-1 message */
-    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
-                                                 &conn->ntlm,
-                                                 &type1msg, &len);
-    if(!result && type1msg) {
-      /* Send the message */
-      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
-
-      if(!result)
-        state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
-    }
-  }
-
-  Curl_safefree(type1msg);
-
-  return result;
-}
-
-/* For NTLM type-2 responses (sent in reponse to our type-1 message) */
-static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
-                                                   int smtpcode,
-                                                   smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *type2msg = NULL;
-  char *type3msg = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Get the type-2 message */
-    smtp_get_message(data->state.buffer, &type2msg);
-
-    /* Decode the type-2 message */
-    result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
-    if(result) {
-      /* Send the cancellation */
-      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
-
-      if(!result)
-        state(conn, SMTP_AUTH_CANCEL);
-    }
-    else {
-      /* Create the type-3 message */
-      result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
-                                                   conn->passwd, &conn->ntlm,
-                                                   &type3msg, &len);
-      if(!result && type3msg) {
-        /* Send the message */
-        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
-
-        if(!result)
-          state(conn, SMTP_AUTH_FINAL);
-      }
-    }
-  }
-
-  Curl_safefree(type3msg);
-
-  return result;
-}
-#endif
-
-#if defined(USE_KERBEROS5)
-/* For AUTH GSSAPI (without initial response) responses */
-static CURLcode smtp_state_auth_gssapi_resp(struct connectdata *conn,
-                                            int smtpcode,
-                                            smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  char *respmsg = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the initial response message */
-    result = Curl_sasl_create_gssapi_user_message(data, conn->user,
-                                                  conn->passwd, "smtp",
-                                                  smtpc->sasl.mutual_auth,
-                                                  NULL,
-                                                  &conn->krb5,
-                                                  &respmsg, &len);
-    if(!result && respmsg) {
-      /* Send the message */
-      result = Curl_pp_sendf(&smtpc->pp, "%s", respmsg);
-
-      if(!result)
-        state(conn, SMTP_AUTH_GSSAPI_TOKEN);
-    }
-  }
-
-  Curl_safefree(respmsg);
-
-  return result;
-}
-
-/* For AUTH GSSAPI user token responses */
-static CURLcode smtp_state_auth_gssapi_token_resp(struct connectdata *conn,
-                                                  int smtpcode,
-                                                  smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  char *chlgmsg = NULL;
-  char *respmsg = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Get the challenge message */
-    smtp_get_message(data->state.buffer, &chlgmsg);
-
-    if(smtpc->sasl.mutual_auth)
-      /* Decode the user token challenge and create the optional response
-         message */
-      result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL,
-                                                    smtpc->sasl.mutual_auth,
-                                                    chlgmsg, &conn->krb5,
-                                                    &respmsg, &len);
-    else
-      /* Decode the security challenge and create the response message */
-      result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
-                                                        &conn->krb5,
-                                                        &respmsg, &len);
-
-    if(result) {
-      if(result == CURLE_BAD_CONTENT_ENCODING) {
-        /* Send the cancellation */
-        result = Curl_pp_sendf(&smtpc->pp, "%s", "*");
-
-        if(!result)
-          state(conn, SMTP_AUTH_CANCEL);
-      }
-    }
-    else {
-      /* Send the response */
-      if(respmsg)
-        result = Curl_pp_sendf(&smtpc->pp, "%s", respmsg);
-      else
-        result = Curl_pp_sendf(&smtpc->pp, "%s", "");
-
-      if(!result)
-        state(conn, (smtpc->sasl.mutual_auth ? SMTP_AUTH_GSSAPI_NO_DATA :
-                                          SMTP_AUTH_FINAL));
-    }
-  }
-
-  Curl_safefree(respmsg);
-
-  return result;
-}
-
-/* For AUTH GSSAPI no data responses */
-static CURLcode smtp_state_auth_gssapi_no_data_resp(struct connectdata *conn,
-                                                    int smtpcode,
-                                                    smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlgmsg = NULL;
-  char *respmsg = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Get the challenge message */
-    smtp_get_message(data->state.buffer, &chlgmsg);
-
-    /* Decode the security challenge and create the response message */
-    result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
-                                                      &conn->krb5,
-                                                      &respmsg, &len);
-    if(result) {
-      if(result == CURLE_BAD_CONTENT_ENCODING) {
-        /* Send the cancellation */
-        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
-
-        if(!result)
-          state(conn, SMTP_AUTH_CANCEL);
-      }
-    }
-    else {
-      /* Send the response */
-      if(respmsg) {
-        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", respmsg);
-
-        if(!result)
-          state(conn, SMTP_AUTH_FINAL);
-      }
-    }
-  }
-
-  Curl_safefree(respmsg);
-
-  return result;
-}
-#endif
-
-/* For AUTH XOAUTH2 (without initial response) responses */
-static CURLcode smtp_state_auth_xoauth2_resp(struct connectdata *conn,
-                                             int smtpcode, smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *xoauth = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the authorisation message */
-    result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
-                                              conn->xoauth2_bearer,
-                                              &xoauth, &len);
-    if(!result && xoauth) {
-      /* Send the message */
-      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", xoauth);
-
-      if(!result)
-        state(conn, SMTP_AUTH_FINAL);
-    }
-  }
-
-  Curl_safefree(xoauth);
-
-  return result;
-}
-
-/* For AUTH cancellation responses */
-static CURLcode smtp_state_auth_cancel_resp(struct connectdata *conn,
-                                            int smtpcode,
-                                            smtpstate instate)
+/* For SASL authentication responses */
+static CURLcode smtp_state_auth_resp(struct connectdata *conn,
+                                     int smtpcode,
+                                     smtpstate instate)
 {
   CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
-  const char *mech = NULL;
-  char *initresp = NULL;
-  size_t len = 0;
-  smtpstate state1 = SMTP_STOP;
-  smtpstate state2 = SMTP_STOP;
+  saslprogress progress;
 
-  (void)smtpcode;
   (void)instate; /* no use for this yet */
 
-  /* Remove the offending mechanism from the supported list */
-  smtpc->sasl.authmechs ^= smtpc->sasl.authused;
-
-  /* Calculate alternative SASL login details */
-  result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
-                                  &state2);
-
-  if(!result) {
-    /* Do we have any mechanisms left? */
-    if(mech) {
-      /* Retry SASL based authentication */
-      result = smtp_perform_auth(conn, mech, initresp, len, state1, state2);
-
-      Curl_safefree(initresp);
-    }
-    else {
+  result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress);
+  if(!result)
+    switch(progress) {
+    case SASL_DONE:
+      state(conn, SMTP_STOP);  /* Authenticated */
+      break;
+    case SASL_IDLE:            /* No mechanism left after cancellation */
       failf(data, "Authentication cancelled");
-
       result = CURLE_LOGIN_DENIED;
+      break;
+    default:
+      break;
     }
-  }
-
-  return result;
-}
-
-/* For final responses in the AUTH sequence */
-static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
-                                           int smtpcode,
-                                           smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 235) {
-    failf(data, "Authentication failed: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else
-    /* End of connect phase */
-    state(conn, SMTP_STOP);
 
   return result;
 }
@@ -1582,69 +1041,8 @@ static CURLcode smtp_statemach_act(struct connectdata *conn)
       result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
       break;
 
-    case SMTP_AUTH_PLAIN:
-      result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_LOGIN:
-      result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_LOGIN_PASSWD:
-      result = smtp_state_auth_login_password_resp(conn, smtpcode,
-                                                   smtpc->state);
-      break;
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-    case SMTP_AUTH_CRAMMD5:
-      result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_DIGESTMD5:
-      result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_DIGESTMD5_RESP:
-      result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
-      break;
-#endif
-
-#ifdef USE_NTLM
-    case SMTP_AUTH_NTLM:
-      result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_NTLM_TYPE2MSG:
-      result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
-                                                  smtpc->state);
-      break;
-#endif
-
-#if defined(USE_KERBEROS5)
-    case SMTP_AUTH_GSSAPI:
-      result = smtp_state_auth_gssapi_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_GSSAPI_TOKEN:
-      result = smtp_state_auth_gssapi_token_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_GSSAPI_NO_DATA:
-      result = smtp_state_auth_gssapi_no_data_resp(conn, smtpcode,
-                                                   smtpc->state);
-      break;
-#endif
-
-    case SMTP_AUTH_XOAUTH2:
-      result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_CANCEL:
-      result = smtp_state_auth_cancel_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_FINAL:
-      result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
+    case SMTP_AUTH:
+      result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
       break;
 
     case SMTP_COMMAND:
@@ -1758,7 +1156,7 @@ static CURLcode smtp_connect(struct connectdata *conn, bool *done)
   pp->conn = conn;
 
   /* Initialize the SASL storage */
-  Curl_sasl_init(&smtpc->sasl);
+  Curl_sasl_init(&smtpc->sasl, &saslsmtp);
 
   /* Initialise the pingpong layer */
   Curl_pp_init(pp);
@@ -2172,110 +1570,6 @@ static CURLcode smtp_parse_custom_request(struct connectdata *conn)
   return result;
 }
 
-/***********************************************************************
- *
- * smtp_calc_sasl_details()
- *
- * Calculate the required login details for SASL authentication.
- */
-static CURLcode smtp_calc_sasl_details(struct connectdata *conn,
-                                       const char **mech,
-                                       char **initresp, size_t *len,
-                                       smtpstate *state1, smtpstate *state2)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-
-  /* Calculate the supported authentication mechanism, by decreasing order of
-     security, as well as the initial response where appropriate */
-#if defined(USE_KERBEROS5)
-  if((smtpc->sasl.authmechs & SASL_MECH_GSSAPI) &&
-     (smtpc->sasl.prefmech & SASL_MECH_GSSAPI)) {
-    smtpc->sasl.mutual_auth = FALSE; /* TODO: Calculate mutual auth. */
-
-    *mech = SASL_MECH_STRING_GSSAPI;
-    *state1 = SMTP_AUTH_GSSAPI;
-    *state2 = SMTP_AUTH_GSSAPI_TOKEN;
-    smtpc->sasl.authused = SASL_MECH_GSSAPI;
-
-    if(data->set.sasl_ir)
-      result = Curl_sasl_create_gssapi_user_message(data, conn->user,
-                                                    conn->passwd, "smtp",
-                                                    smtpc->sasl.mutual_auth,
-                                                    NULL, &conn->krb5,
-                                                    initresp, len);
-    }
-  else
-#endif
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-  if((smtpc->sasl.authmechs & SASL_MECH_DIGEST_MD5) &&
-     (smtpc->sasl.prefmech & SASL_MECH_DIGEST_MD5)) {
-    *mech = SASL_MECH_STRING_DIGEST_MD5;
-    *state1 = SMTP_AUTH_DIGESTMD5;
-    smtpc->sasl.authused = SASL_MECH_DIGEST_MD5;
-  }
-  else if((smtpc->sasl.authmechs & SASL_MECH_CRAM_MD5) &&
-          (smtpc->sasl.prefmech & SASL_MECH_CRAM_MD5)) {
-    *mech = SASL_MECH_STRING_CRAM_MD5;
-    *state1 = SMTP_AUTH_CRAMMD5;
-    smtpc->sasl.authused = SASL_MECH_CRAM_MD5;
-  }
-  else
-#endif
-#ifdef USE_NTLM
-  if((smtpc->sasl.authmechs & SASL_MECH_NTLM) &&
-     (smtpc->sasl.prefmech & SASL_MECH_NTLM)) {
-    *mech = SASL_MECH_STRING_NTLM;
-    *state1 = SMTP_AUTH_NTLM;
-    *state2 = SMTP_AUTH_NTLM_TYPE2MSG;
-    smtpc->sasl.authused = SASL_MECH_NTLM;
-
-    if(data->set.sasl_ir)
-      result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
-                                                   &conn->ntlm,
-                                                   initresp, len);
-    }
-  else
-#endif
-  if(((smtpc->sasl.authmechs & SASL_MECH_XOAUTH2) &&
-      (smtpc->sasl.prefmech & SASL_MECH_XOAUTH2) &&
-      (smtpc->sasl.prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
-    *mech = SASL_MECH_STRING_XOAUTH2;
-    *state1 = SMTP_AUTH_XOAUTH2;
-    *state2 = SMTP_AUTH_FINAL;
-    smtpc->sasl.authused = SASL_MECH_XOAUTH2;
-
-    if(data->set.sasl_ir)
-      result = Curl_sasl_create_xoauth2_message(data, conn->user,
-                                                conn->xoauth2_bearer,
-                                                initresp, len);
-  }
-  else if((smtpc->sasl.authmechs & SASL_MECH_LOGIN) &&
-          (smtpc->sasl.prefmech & SASL_MECH_LOGIN)) {
-    *mech = SASL_MECH_STRING_LOGIN;
-    *state1 = SMTP_AUTH_LOGIN;
-    *state2 = SMTP_AUTH_LOGIN_PASSWD;
-    smtpc->sasl.authused = SASL_MECH_LOGIN;
-
-    if(data->set.sasl_ir)
-      result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
-  }
-  else if((smtpc->sasl.authmechs & SASL_MECH_PLAIN) &&
-          (smtpc->sasl.prefmech & SASL_MECH_PLAIN)) {
-    *mech = SASL_MECH_STRING_PLAIN;
-    *state1 = SMTP_AUTH_PLAIN;
-    *state2 = SMTP_AUTH_FINAL;
-    smtpc->sasl.authused = SASL_MECH_PLAIN;
-
-    if(data->set.sasl_ir)
-      result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
-                                              initresp, len);
-  }
-
-  return result;
-}
-
 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread)
 {
   /* When sending a SMTP payload we must detect CRLF. sequences making sure
index a237d3e5c969982c6cb61e7bc4288bd4b78973d1..9fbe0c5bf9dea1c05bc217141b1893412c7f05ba 100644 (file)
@@ -37,20 +37,7 @@ typedef enum {
   SMTP_STARTTLS,
   SMTP_UPGRADETLS,  /* asynchronously upgrade the connection to SSL/TLS
                        (multi mode only) */
-  SMTP_AUTH_PLAIN,
-  SMTP_AUTH_LOGIN,
-  SMTP_AUTH_LOGIN_PASSWD,
-  SMTP_AUTH_CRAMMD5,
-  SMTP_AUTH_DIGESTMD5,
-  SMTP_AUTH_DIGESTMD5_RESP,
-  SMTP_AUTH_NTLM,
-  SMTP_AUTH_NTLM_TYPE2MSG,
-  SMTP_AUTH_GSSAPI,
-  SMTP_AUTH_GSSAPI_TOKEN,
-  SMTP_AUTH_GSSAPI_NO_DATA,
-  SMTP_AUTH_XOAUTH2,
-  SMTP_AUTH_CANCEL,
-  SMTP_AUTH_FINAL,
+  SMTP_AUTH,
   SMTP_COMMAND,     /* VRFY, EXPN, NOOP, RSET and HELP */
   SMTP_MAIL,        /* MAIL FROM */
   SMTP_RCPT,        /* RCPT TO */