]> granicus.if.org Git - curl/commitdiff
SASL: common URL option and auth capabilities decoders for all protocols
authorPatrick Monnerat <pm@datasphere.ch>
Tue, 20 Jan 2015 14:27:25 +0000 (15:27 +0100)
committerPatrick Monnerat <pm@datasphere.ch>
Tue, 20 Jan 2015 14:27:25 +0000 (15:27 +0100)
lib/curl_sasl.c
lib/curl_sasl.h
lib/imap.c
lib/imap.h
lib/pop3.c
lib/pop3.h
lib/smtp.c

index b944aa2df5b8fe140211f50226c74f94cd5e335d..7cb575b33b07dfba9e72765846bdeb823650a860 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -41,6 +41,7 @@
 #include "warnless.h"
 #include "curl_memory.h"
 #include "strtok.h"
+#include "strequal.h"
 #include "rawstr.h"
 #include "non-ascii.h" /* included for Curl_convert_... prototypes */
 
     return result; \
   }
 
+
+/* Supported mechanisms */
+const struct {
+  const char *  name;  /* Name */
+  size_t        len;   /* Name length */
+  unsigned int  bit;   /* Flag bit */
+} mechtable[] = {
+  { "LOGIN",      5,  SASL_MECH_LOGIN },
+  { "PLAIN",      5,  SASL_MECH_PLAIN },
+  { "CRAM-MD5",   8,  SASL_MECH_CRAM_MD5 },
+  { "DIGEST-MD5", 10, SASL_MECH_DIGEST_MD5 },
+  { "GSSAPI",     6,  SASL_MECH_GSSAPI },
+  { "EXTERNAL",   8,  SASL_MECH_EXTERNAL },
+  { "NTLM",       4,  SASL_MECH_NTLM },
+  { "XOAUTH2",    7,  SASL_MECH_XOAUTH2 },
+  { ZERO_NULL,    0,  0 }
+};
+
 /*
  * Return 0 on success and then the buffers are filled in fine.
  *
@@ -248,7 +267,7 @@ static CURLcode sasl_digest_get_qop_values(const char *options, int *value)
  *
  * Parameters:
  *
- * serivce  [in] - The service type such as www, smtp, pop or imap.
+ * service  [in] - The service type such as www, smtp, pop or imap.
  * host     [in] - The host name or realm.
  *
  * Returns a pointer to the newly allocated SPN.
@@ -1180,3 +1199,83 @@ void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
   (void)authused;
 #endif
 }
+
+/*
+ * Curl_sasl_decode_mech()
+ *
+ * Convert an SASL mechanism name to a token.
+ *
+ * Parameters:
+ *
+ * ptr    [in]     - The mechanism string.
+ * maxlen [in]     - Maximum mechanism string length.
+ * len    [out]    - If not NULL, effective name length.
+ *
+ * Return the SASL mechanism token or 0 if no match.
+ */
+unsigned int
+Curl_sasl_decode_mech(const char *ptr, size_t maxlen, size_t *len)
+{
+  unsigned int i;
+  char c;
+
+  for(i = 0; mechtable[i].name; i++) {
+    if(maxlen >= mechtable[i].len &&
+       !memcmp(ptr, mechtable[i].name, mechtable[i].len)) {
+      if(len)
+        *len = mechtable[i].len;
+      if(maxlen == mechtable[i].len)
+        return mechtable[i].bit;
+      c = ptr[mechtable[i].len];
+      if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_')
+        return mechtable[i].bit;
+    }
+  }
+
+  return 0;
+}
+
+/*
+ * Curl_sasl_parse_url_auth_option()
+ *
+ * Parse the URL login options.
+ */
+CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
+                                         const char *value, size_t len)
+{
+  CURLcode result = CURLE_OK;
+  unsigned int mechbit;
+  size_t llen;
+
+  if(!len)
+    return CURLE_URL_MALFORMAT;
+
+    if(sasl->resetprefs) {
+      sasl->resetprefs = FALSE;
+      sasl->prefmech = SASL_AUTH_NONE;
+    }
+
+    if(strnequal(value, "*", len))
+      sasl->prefmech = SASL_AUTH_ANY;
+    else if((mechbit = Curl_sasl_decode_mech(value, len, &llen)) &&
+      llen == len)
+      sasl->prefmech |= mechbit;
+    else
+      result = CURLE_URL_MALFORMAT;
+
+  return result;
+}
+
+/*
+ * Curl_sasl_init()
+ *
+ * Initializes an SASL structure.
+ */
+void Curl_sasl_init(struct SASL *sasl)
+{
+  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) */
+}
index a83a326a4301456a5bfc7a0648377a5f1ae013a3..e043449db44cab00c1250c227b0ecae310871a9f 100644 (file)
@@ -73,6 +73,7 @@ struct SASL {
   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) */
 };
 
@@ -201,4 +202,15 @@ CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data,
    functions */
 void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused);
 
+/* Convert a mechanism name to a token */
+unsigned int Curl_sasl_decode_mech(const char *ptr,
+                                   size_t maxlen, size_t *len);
+
+/* Parse the URL login options */
+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);
+
 #endif /* HEADER_CURL_SASL_H */
index 4087fa66abe32ba203bbc6cbe141ce992518f043..965efdf8a089cca7ffd976316de12f9a6ff6376d 100644 (file)
@@ -914,26 +914,16 @@ static CURLcode imap_state_capability_resp(struct connectdata *conn,
 
       /* Do we have a SASL based authentication mechanism? */
       else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
+        size_t llen;
+        unsigned int mechbit;
+
         line += 5;
         wordlen -= 5;
 
         /* Test the word for a matching authentication mechanism */
-        if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
-          imapc->sasl.authmechs |= SASL_MECH_LOGIN;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
-          imapc->sasl.authmechs |= SASL_MECH_PLAIN;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
-          imapc->sasl.authmechs |= SASL_MECH_CRAM_MD5;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
-          imapc->sasl.authmechs |= SASL_MECH_DIGEST_MD5;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
-          imapc->sasl.authmechs |= SASL_MECH_GSSAPI;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
-          imapc->sasl.authmechs |= SASL_MECH_EXTERNAL;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
-          imapc->sasl.authmechs |= SASL_MECH_NTLM;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
-          imapc->sasl.authmechs |= SASL_MECH_XOAUTH2;
+        if((mechbit = Curl_sasl_decode_mech(line, wordlen, &llen)) &&
+           llen == wordlen)
+          imapc->sasl.authmechs |= mechbit;
       }
 
       line += wordlen;
@@ -2061,7 +2051,7 @@ static CURLcode imap_connect(struct connectdata *conn, bool *done)
 
   /* Set the default preferred authentication type and mechanism */
   imapc->preftype = IMAP_TYPE_ANY;
-  imapc->sasl.prefmech = SASL_AUTH_ANY;
+  Curl_sasl_init(&imapc->sasl);
 
   /* Initialise the pingpong layer */
   Curl_pp_init(pp);
@@ -2548,69 +2538,42 @@ static CURLcode imap_parse_url_options(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
   struct imap_conn *imapc = &conn->proto.imapc;
-  const char *options = conn->options;
-  const char *ptr = options;
-  bool reset = TRUE;
+  const char *ptr = conn->options;
+
+  imapc->sasl.resetprefs = TRUE;
 
-  while(ptr && *ptr) {
+  while(!result && ptr && *ptr) {
     const char *key = ptr;
+    const char *value;
 
     while(*ptr && *ptr != '=')
         ptr++;
 
-    if(strnequal(key, "AUTH", 4)) {
-      size_t len = 0;
-      const char *value = ++ptr;
-
-      if(reset) {
-        reset = FALSE;
-        imapc->preftype = IMAP_TYPE_NONE;
-        imapc->sasl.prefmech = SASL_AUTH_NONE;
-      }
+    value = ptr + 1;
 
-      while(*ptr && *ptr != ';') {
-        ptr++;
-        len++;
-      }
-
-      if(strnequal(value, "*", len)) {
-        imapc->preftype = IMAP_TYPE_ANY;
-        imapc->sasl.prefmech = SASL_AUTH_ANY;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) {
-        imapc->preftype = IMAP_TYPE_SASL;
-        imapc->sasl.prefmech |= SASL_MECH_LOGIN;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) {
-        imapc->preftype = IMAP_TYPE_SASL;
-        imapc->sasl.prefmech |= SASL_MECH_PLAIN;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) {
-        imapc->preftype = IMAP_TYPE_SASL;
-        imapc->sasl.prefmech |= SASL_MECH_CRAM_MD5;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) {
-        imapc->preftype = IMAP_TYPE_SASL;
-        imapc->sasl.prefmech |= SASL_MECH_DIGEST_MD5;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) {
-        imapc->preftype = IMAP_TYPE_SASL;
-        imapc->sasl.prefmech |= SASL_MECH_GSSAPI;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) {
-        imapc->preftype = IMAP_TYPE_SASL;
-        imapc->sasl.prefmech |= SASL_MECH_NTLM;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) {
-        imapc->preftype = IMAP_TYPE_SASL;
-        imapc->sasl.prefmech |= SASL_MECH_XOAUTH2;
-      }
+    while(*ptr && *ptr != ';')
+      ptr++;
 
-      if(*ptr == ';')
-        ptr++;
-    }
+    if(strnequal(key, "AUTH=", 5))
+      result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
+                                               value, ptr - value);
     else
       result = CURLE_URL_MALFORMAT;
+
+    if(*ptr == ';')
+      ptr++;
+  }
+
+  switch(imapc->sasl.prefmech) {
+  case SASL_AUTH_NONE:
+    imapc->preftype = IMAP_TYPE_NONE;
+    break;
+  case SASL_AUTH_ANY:
+    imapc->preftype = IMAP_TYPE_ANY;
+    break;
+  default:
+    imapc->preftype = IMAP_TYPE_SASL;
+    break;
   }
 
   return result;
index 81af6a290263989e7a948984cff047bf1a7edcc7..22e6ada1c6e32ed61b91c91b7a28b883be3f3a0a 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2009 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2009 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
index f40c9218bf1e9d5742d7a3946c92c918cd6b1c09..f5828e515f32eacb5d9bf327d258bec0bdfd58fd 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -726,6 +726,9 @@ static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
 
       /* Loop through the data line */
       for(;;) {
+        size_t llen;
+        unsigned int mechbit;
+
         while(len &&
               (*line == ' ' || *line == '\t' ||
                *line == '\r' || *line == '\n')) {
@@ -744,22 +747,9 @@ static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
           wordlen++;
 
         /* Test the word for a matching authentication mechanism */
-        if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
-          pop3c->sasl.authmechs |= SASL_MECH_LOGIN;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
-          pop3c->sasl.authmechs |= SASL_MECH_PLAIN;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
-          pop3c->sasl.authmechs |= SASL_MECH_CRAM_MD5;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
-          pop3c->sasl.authmechs |= SASL_MECH_DIGEST_MD5;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
-          pop3c->sasl.authmechs |= SASL_MECH_GSSAPI;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
-          pop3c->sasl.authmechs |= SASL_MECH_EXTERNAL;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
-          pop3c->sasl.authmechs |= SASL_MECH_NTLM;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
-          pop3c->sasl.authmechs |= SASL_MECH_XOAUTH2;
+        if((mechbit = Curl_sasl_decode_mech(line, wordlen, &llen)) &&
+           llen == wordlen)
+          pop3c->sasl.authmechs |= mechbit;
 
         line += wordlen;
         len -= wordlen;
@@ -1727,7 +1717,7 @@ static CURLcode pop3_connect(struct connectdata *conn, bool *done)
 
   /* Set the default preferred authentication type and mechanism */
   pop3c->preftype = POP3_TYPE_ANY;
-  pop3c->sasl.prefmech = SASL_AUTH_ANY;
+  Curl_sasl_init(&pop3c->sasl);
 
   /* Initialise the pingpong layer */
   Curl_pp_init(pp);
@@ -1994,75 +1984,52 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
   struct pop3_conn *pop3c = &conn->proto.pop3c;
-  const char *options = conn->options;
-  const char *ptr = options;
-  bool reset = TRUE;
+  const char *ptr = conn->options;
+
+  pop3c->sasl.resetprefs = TRUE;
 
-  while(ptr && *ptr) {
+  while(!result && ptr && *ptr) {
     const char *key = ptr;
+    const char *value;
 
     while(*ptr && *ptr != '=')
         ptr++;
 
-    if(strnequal(key, "AUTH", 4)) {
-      size_t len = 0;
-      const char *value = ++ptr;
+    value = ptr + 1;
 
-      if(reset) {
-        reset = FALSE;
-        pop3c->preftype = POP3_TYPE_NONE;
-        pop3c->sasl.prefmech = SASL_AUTH_NONE;
-      }
+    while(*ptr && *ptr != ';')
+      ptr++;
 
-      while(*ptr && *ptr != ';') {
-        ptr++;
-        len++;
-      }
+    if(strnequal(key, "AUTH=", 5)) {
+      result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
+                                               value, ptr - value);
 
-      if(strnequal(value, "*", len)) {
-        pop3c->preftype = POP3_TYPE_ANY;
-        pop3c->sasl.prefmech = SASL_AUTH_ANY;
-      }
-      else if(strnequal(value, "+APOP", len)) {
+      if(result && strnequal(value, "+APOP", ptr - value)) {
         pop3c->preftype = POP3_TYPE_APOP;
         pop3c->sasl.prefmech = SASL_AUTH_NONE;
+        result = CURLE_OK;
       }
-      else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) {
-        pop3c->preftype = POP3_TYPE_SASL;
-        pop3c->sasl.prefmech |= SASL_MECH_LOGIN;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) {
-        pop3c->preftype = POP3_TYPE_SASL;
-        pop3c->sasl.prefmech |= SASL_MECH_PLAIN;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) {
-        pop3c->preftype = POP3_TYPE_SASL;
-        pop3c->sasl.prefmech |= SASL_MECH_CRAM_MD5;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) {
-        pop3c->preftype = POP3_TYPE_SASL;
-        pop3c->sasl.prefmech |= SASL_MECH_DIGEST_MD5;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) {
-        pop3c->preftype = POP3_TYPE_SASL;
-        pop3c->sasl.prefmech |= SASL_MECH_GSSAPI;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) {
-        pop3c->preftype = POP3_TYPE_SASL;
-        pop3c->sasl.prefmech |= SASL_MECH_NTLM;
-      }
-      else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) {
-        pop3c->preftype = POP3_TYPE_SASL;
-        pop3c->sasl.prefmech |= SASL_MECH_XOAUTH2;
-      }
-
-      if(*ptr == ';')
-        ptr++;
     }
     else
       result = CURLE_URL_MALFORMAT;
+
+    if(*ptr == ';')
+      ptr++;
   }
 
+  if(pop3c->preftype != POP3_TYPE_APOP)
+    switch(pop3c->sasl.prefmech) {
+    case SASL_AUTH_NONE:
+      pop3c->preftype = POP3_TYPE_NONE;
+      break;
+    case SASL_AUTH_ANY:
+      pop3c->preftype = POP3_TYPE_ANY;
+      break;
+    default:
+      pop3c->preftype = POP3_TYPE_SASL;
+      break;
+    }
+
   return result;
 }
 
index 7b4d21f03e886292a2a53c09f36ce2f0d9406da0..0cb5852d89b7f0a184a5628909fbc64785db6ae0 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2009 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2009 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
index 5f75f524b3fbf5909a50288ec9c1e147ef58212e..e5ddedb02dc1f47c6450747df9e9ec8172cbe1ed 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -753,6 +753,9 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
 
       /* Loop through the data line */
       for(;;) {
+        size_t llen;
+        unsigned int mechbit;
+
         while(len &&
               (*line == ' ' || *line == '\t' ||
                *line == '\r' || *line == '\n')) {
@@ -771,22 +774,9 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
           wordlen++;
 
         /* Test the word for a matching authentication mechanism */
-        if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
-          smtpc->sasl.authmechs |= SASL_MECH_LOGIN;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
-          smtpc->sasl.authmechs |= SASL_MECH_PLAIN;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
-          smtpc->sasl.authmechs |= SASL_MECH_CRAM_MD5;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
-          smtpc->sasl.authmechs |= SASL_MECH_DIGEST_MD5;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
-          smtpc->sasl.authmechs |= SASL_MECH_GSSAPI;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
-          smtpc->sasl.authmechs |= SASL_MECH_EXTERNAL;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
-          smtpc->sasl.authmechs |= SASL_MECH_NTLM;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
-          smtpc->sasl.authmechs |= SASL_MECH_XOAUTH2;
+        if((mechbit = Curl_sasl_decode_mech(line, wordlen, &llen)) &&
+           llen == wordlen)
+          smtpc->sasl.authmechs |= mechbit;
 
         line += wordlen;
         len -= wordlen;
@@ -1767,8 +1757,8 @@ static CURLcode smtp_connect(struct connectdata *conn, bool *done)
   pp->endofresp = smtp_endofresp;
   pp->conn = conn;
 
-  /* Set the default preferred authentication mechanism */
-  smtpc->sasl.prefmech = SASL_AUTH_ANY;
+  /* Initialize the SASL storage */
+  Curl_sasl_init(&smtpc->sasl);
 
   /* Initialise the pingpong layer */
   Curl_pp_init(pp);
@@ -2107,52 +2097,30 @@ static CURLcode smtp_parse_url_options(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
-  const char *options = conn->options;
-  const char *ptr = options;
-  bool reset = TRUE;
+  const char *ptr = conn->options;
+
+  smtpc->sasl.resetprefs = TRUE;
 
-  while(ptr && *ptr) {
+  while(!result && ptr && *ptr) {
     const char *key = ptr;
+    const char *value;
 
     while(*ptr && *ptr != '=')
         ptr++;
 
-    if(strnequal(key, "AUTH", 4)) {
-      size_t len = 0;
-      const char *value = ++ptr;
+    value = ptr + 1;
 
-      if(reset) {
-        reset = FALSE;
-        smtpc->sasl.prefmech = SASL_AUTH_NONE;
-      }
+    while(*ptr && *ptr != ';')
+      ptr++;
 
-      while(*ptr && *ptr != ';') {
-        ptr++;
-        len++;
-      }
-
-      if(strnequal(value, "*", len))
-        smtpc->sasl.prefmech = SASL_AUTH_ANY;
-      else if(strnequal(value, SASL_MECH_STRING_LOGIN, len))
-        smtpc->sasl.prefmech |= SASL_MECH_LOGIN;
-      else if(strnequal(value, SASL_MECH_STRING_PLAIN, len))
-        smtpc->sasl.prefmech |= SASL_MECH_PLAIN;
-      else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len))
-        smtpc->sasl.prefmech |= SASL_MECH_CRAM_MD5;
-      else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len))
-        smtpc->sasl.prefmech |= SASL_MECH_DIGEST_MD5;
-      else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len))
-        smtpc->sasl.prefmech |= SASL_MECH_GSSAPI;
-      else if(strnequal(value, SASL_MECH_STRING_NTLM, len))
-        smtpc->sasl.prefmech |= SASL_MECH_NTLM;
-      else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len))
-        smtpc->sasl.prefmech |= SASL_MECH_XOAUTH2;
-
-      if(*ptr == ';')
-        ptr++;
-    }
+    if(strnequal(key, "AUTH=", 5))
+      result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
+                                               value, ptr - value);
     else
       result = CURLE_URL_MALFORMAT;
+
+    if(*ptr == ';')
+      ptr++;
   }
 
   return result;