Commit cfb6f032 authored by Steve Holme's avatar Steve Holme
Browse files

imap: Added support for sasl plain text authentication

parent 11f55a96
Loading
Loading
Loading
Loading
+115 −2
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 * RFC2222 Simple Authentication and Security Layer (SASL)
 * RFC3501 IMAPv4 protocol
 * RFC4616 PLAIN authentication
 * RFC5092 IMAP URL Scheme
 *
 ***************************************************************************/
@@ -430,6 +432,8 @@ static void state(struct connectdata *conn,
    "STARTTLS",
    "UPGRADETLS",
    "CAPABILITY",
    "AUTHENTICATE_PLAIN",
    "AUTHENTICATE",
    "LOGIN",
    "SELECT",
    "FETCH",
@@ -452,6 +456,7 @@ static CURLcode imap_state_capability(struct connectdata *conn)
  const char *str;

  imapc->authmechs = 0;         /* No known authentication mechanisms yet */
  imapc->authused = 0;          /* Clear the authentication mechanism used */

  /* Check we have a username and password to authenticate with and end the
     connect phase if we don't */
@@ -497,6 +502,37 @@ static CURLcode imap_state_login(struct connectdata *conn)
  return CURLE_OK;
}

static CURLcode imap_authenticate(struct connectdata *conn)
{
  CURLcode result = CURLE_OK;
  struct imap_conn *imapc = &conn->proto.imapc;
  const char *mech = NULL;
  imapstate authstate = IMAP_STOP;

  /* Check supported authentication mechanisms by decreasing order of
     security */
  if(imapc->authmechs & SASL_MECH_PLAIN) {
    mech = "PLAIN";
    authstate = IMAP_AUTHENTICATE_PLAIN;
    imapc->authused = SASL_MECH_PLAIN;
  }
  else {
    infof(conn->data, "No known authentication mechanisms supported!\n");
    result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported */
  }

  if(!result) {
    const char *str = getcmdid(conn);

    result = imap_sendf(conn, str, "%s AUTHENTICATE %s", str, mech);

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

  return result;
}

/* For the IMAP "protocol connect" and "doing" phases only */
static int imap_getsock(struct connectdata *conn,
                        curl_socket_t *socks,
@@ -597,10 +633,76 @@ static CURLcode imap_state_capability_resp(struct connectdata *conn,
                                           int imapcode,
                                           imapstate instate)
{
  (void)imapcode; /* no use for this yet */
  CURLcode result = CURLE_OK;
  struct imap_conn *imapc = &conn->proto.imapc;

  (void)instate; /* no use for this yet */

  if(imapcode == 'O' && imapc->authmechs)
    result = imap_authenticate(conn);
  else
    result = imap_state_login(conn);

  return result;
}

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

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

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

      Curl_safefree(plainauth);
    }
  }

  return result;
}


/* For final responses to 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 */

  return imap_state_login(conn);
  if(imapcode != 'O') {
    failf(data, "Authentication failed: %d", imapcode);
    result = CURLE_LOGIN_DENIED;
  }

  /* End of connect phase */
  state(conn, IMAP_STOP);

  return result;
}

/* For LOGIN responses */
@@ -807,6 +909,14 @@ static CURLcode imap_statemach_act(struct connectdata *conn)
      result = imap_state_capability_resp(conn, imapcode, imapc->state);
      break;

    case IMAP_AUTHENTICATE_PLAIN:
      result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
      break;

    case IMAP_AUTHENTICATE:
      result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
      break;

    case IMAP_LOGIN:
      result = imap_state_login_resp(conn, imapcode, imapc->state);
      break;
@@ -1117,6 +1227,9 @@ static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
  /* Disconnect from the server */
  Curl_pp_disconnect(&imapc->pp);

  /* Cleanup the SASL module */
  Curl_sasl_cleanup(conn, imapc->authused);

  /* Cleanup our connection based variables */
  Curl_safefree(imapc->mailbox);

+3 −0
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ typedef enum {
  IMAP_UPGRADETLS,   /* asynchronously upgrade the connection to SSL/TLS
                       (multi mode only) */
  IMAP_CAPABILITY,
  IMAP_AUTHENTICATE_PLAIN,
  IMAP_AUTHENTICATE,
  IMAP_LOGIN,
  IMAP_SELECT,
  IMAP_FETCH,
@@ -48,6 +50,7 @@ struct imap_conn {
  struct pingpong pp;
  char *mailbox;          /* Message ID to fetch */
  unsigned int authmechs; /* Accepted authentication mechanisms */
  unsigned int authused;  /* Auth mechanism used for the connection */
  imapstate state;        /* Always use imap.c:state() to change state! */
  int cmdid;              /* Next command ID */
  const char *idstr;      /* String based response ID to wait for */