Commit 34122800 authored by Kyle L. Huff's avatar Kyle L. Huff Committed by Steve Holme
Browse files

imap: added basic SASL XOAUTH2 support

Added the ability to use an XOAUTH2 bearer token [RFC6750] with IMAP 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
"A001 AUTHENTICATE XOAUTH2 <bearer token>".
parent 7f41eab3
Loading
Loading
Loading
Loading
+63 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
 * RFC4616 PLAIN authentication
 * RFC4959 IMAP Extension for SASL Initial Client Response
 * RFC5092 IMAP URL Scheme
 * RFC6749 OAuth 2.0 Authorization Framework
 *
 ***************************************************************************/

@@ -344,6 +345,7 @@ static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
      case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
      case IMAP_AUTHENTICATE_NTLM:
      case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
      case IMAP_AUTHENTICATE_XOAUTH2:
      case IMAP_AUTHENTICATE_FINAL:
      case IMAP_APPEND:
        *resp = '+';
@@ -386,6 +388,7 @@ static void state(struct connectdata *conn, imapstate newstate)
    "AUTHENTICATE_DIGESTMD5_RESP",
    "AUTHENTICATE_NTLM",
    "AUTHENTICATE_NTLM_TYPE2MSG",
    "AUTHENTICATE_XOAUTH2",
    "AUTHENTICATE_FINAL",
    "LOGIN",
    "LIST",
@@ -575,7 +578,20 @@ static CURLcode imap_perform_authenticate(struct connectdata *conn)
  }
  else
#endif
  if((imapc->authmechs & SASL_MECH_LOGIN) &&

  if((imapc->authmechs & SASL_MECH_XOAUTH2) &&
     (imapc->prefmech & SASL_MECH_XOAUTH2)) {
    mech = "XOAUTH2";
    state1 = IMAP_AUTHENTICATE_XOAUTH2;
    state2 = IMAP_AUTHENTICATE_FINAL;
    imapc->authused = SASL_MECH_XOAUTH2;

    if(imapc->ir_supported || data->set.sasl_ir)
      result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
                                                conn->xoauth2_bearer,
                                                &initresp, &len);
  }
  else if((imapc->authmechs & SASL_MECH_LOGIN) &&
     (imapc->prefmech & SASL_MECH_LOGIN)) {
    mech = "LOGIN";
    state1 = IMAP_AUTHENTICATE_LOGIN;
@@ -879,6 +895,8 @@ static CURLcode imap_state_capability_resp(struct connectdata *conn,
          imapc->authmechs |= SASL_MECH_EXTERNAL;
        else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
          imapc->authmechs |= SASL_MECH_NTLM;
        else if(wordlen == 7 && !memcmp(line, "XOAUTH2", 7))
          imapc->authmechs |= SASL_MECH_XOAUTH2;
      }

      line += wordlen;
@@ -1244,6 +1262,44 @@ static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
}
#endif

/* For AUTH 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);

    /* Send the message */
    if(!result) {
      if(xoauth) {
        result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", xoauth);

        if(!result)
          state(conn, IMAP_AUTHENTICATE_FINAL);
      }

      Curl_safefree(xoauth);
    }
  }

  return result;
}

/* For final responses to the AUTHENTICATE sequence */
static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
                                           int imapcode,
@@ -1593,6 +1649,10 @@ static CURLcode imap_statemach_act(struct connectdata *conn)
      break;
#endif

    case IMAP_AUTHENTICATE_XOAUTH2:
      result = imap_state_auth_xoauth2_resp(conn, imapcode, imapc->state);
      break;

    case IMAP_AUTHENTICATE_FINAL:
      result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
      break;
@@ -2221,6 +2281,8 @@ static CURLcode imap_parse_url_options(struct connectdata *conn)
        imapc->prefmech = SASL_MECH_GSSAPI;
      else if(strequal(value, "NTLM"))
        imapc->prefmech = SASL_MECH_NTLM;
      else if(strequal(value, "XOAUTH2"))
        imapc->prefmech = SASL_MECH_XOAUTH2;
      else
        imapc->prefmech = SASL_AUTH_NONE;
    }
+1 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ typedef enum {
  IMAP_AUTHENTICATE_DIGESTMD5_RESP,
  IMAP_AUTHENTICATE_NTLM,
  IMAP_AUTHENTICATE_NTLM_TYPE2MSG,
  IMAP_AUTHENTICATE_XOAUTH2,
  IMAP_AUTHENTICATE_FINAL,
  IMAP_LOGIN,
  IMAP_LIST,