From 90ab65c632ec0405893466637c7971e327f1067a Mon Sep 17 00:00:00 2001
From: "Kyle L. Huff" <kyle.huff@curetheitch.com>
Date: Sun, 25 Aug 2013 13:17:58 -0400
Subject: [PATCH] smtp: added basic SASL XOAUTH2 support

Added the ability to use an XOAUTH2 bearer token [RFC6750] with SMTP for
authentication using RFC6749 "OAuth 2.0 Authorization Framework".

The bearer token is expected to be valid for the user specified in
conn->user. If CURLOPT_XOAUTH2_BEARER is defined and the connection has
an advertised auth mechanism of "XOAUTH2", the user and access token are
formatted as a base64 encoded string and sent to the server as
"AUTH XOAUTH2 <bearer token>".
---
 lib/smtp.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 lib/smtp.h |  1 +
 2 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/lib/smtp.c b/lib/smtp.c
index a56f54921c..f2c79794cc 100644
--- a/lib/smtp.c
+++ b/lib/smtp.c
@@ -26,6 +26,7 @@
  * RFC4616 PLAIN authentication
  * RFC4954 SMTP Authentication
  * RFC5321 SMTP protocol
+ * RFC6749 OAuth 2.0 Authorization Framework
  * Draft   SMTP URL Interface
  *
  ***************************************************************************/
@@ -290,6 +291,8 @@ static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
           smtpc->authmechs |= SASL_MECH_EXTERNAL;
         else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
           smtpc->authmechs |= SASL_MECH_NTLM;
+        else if(wordlen == 7 && !memcmp(line, "XOAUTH2", 7))
+          smtpc->authmechs |= SASL_MECH_XOAUTH2;
 
         line += wordlen;
         len -= wordlen;
@@ -326,6 +329,7 @@ static void state(struct connectdata *conn, smtpstate newstate)
     "AUTH_DIGESTMD5_RESP",
     "AUTH_NTLM",
     "AUTH_NTLM_TYPE2MSG",
+    "AUTH_XOAUTH2",
     "AUTH_FINAL",
     "MAIL",
     "RCPT",
@@ -496,7 +500,20 @@ static CURLcode smtp_perform_authenticate(struct connectdata *conn)
     }
   else
 #endif
-  if((smtpc->authmechs & SASL_MECH_LOGIN) &&
+
+  if((smtpc->authmechs & SASL_MECH_XOAUTH2) &&
+     (smtpc->prefmech & SASL_MECH_XOAUTH2)) {
+    mech = "XOAUTH2";
+    state1 = SMTP_AUTH_XOAUTH2;
+    state2 = SMTP_AUTH_FINAL;
+    smtpc->authused = SASL_MECH_XOAUTH2;
+
+    if(data->set.sasl_ir)
+      result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
+                                                conn->xoauth2_bearer,
+                                                &initresp, &len);
+  }
+  else if((smtpc->authmechs & SASL_MECH_LOGIN) &&
      (smtpc->prefmech & SASL_MECH_LOGIN)) {
     mech = "LOGIN";
     state1 = SMTP_AUTH_LOGIN;
@@ -1088,6 +1105,43 @@ static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
 }
 #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);
+
+    /* Send the message */
+    if(!result) {
+      if(xoauth) {
+        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", xoauth);
+
+        if(!result)
+          state(conn, SMTP_AUTH_FINAL);
+      }
+
+      Curl_safefree(xoauth);
+    }
+  }
+
+  return result;
+}
+
 /* For the final responses to the AUTH sequence */
 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
                                            int smtpcode,
@@ -1296,6 +1350,10 @@ static CURLcode smtp_statemach_act(struct connectdata *conn)
       break;
 #endif
 
+    case SMTP_AUTH_XOAUTH2:
+      result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state);
+      break;
+
     case SMTP_AUTH_FINAL:
       result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
       break;
@@ -1738,6 +1796,8 @@ static CURLcode smtp_parse_url_options(struct connectdata *conn)
         smtpc->prefmech = SASL_MECH_GSSAPI;
       else if(strequal(value, "NTLM"))
         smtpc->prefmech = SASL_MECH_NTLM;
+      else if(strequal(value, "XOAUTH2"))
+        smtpc->prefmech = SASL_MECH_XOAUTH2;
       else
         smtpc->prefmech = SASL_AUTH_NONE;
     }
diff --git a/lib/smtp.h b/lib/smtp.h
index 4aff0c5f92..14429a5e7e 100644
--- a/lib/smtp.h
+++ b/lib/smtp.h
@@ -44,6 +44,7 @@ typedef enum {
   SMTP_AUTH_DIGESTMD5_RESP,
   SMTP_AUTH_NTLM,
   SMTP_AUTH_NTLM_TYPE2MSG,
+  SMTP_AUTH_XOAUTH2,
   SMTP_AUTH_FINAL,
   SMTP_MAIL,        /* MAIL FROM */
   SMTP_RCPT,        /* RCPT TO */
-- 
GitLab