Commit e1ea18f9 authored by Patrick Monnerat's avatar Patrick Monnerat
Browse files

SASL: common URL option and auth capabilities decoders for all protocols

parent 5f09cbcd
Loading
Loading
Loading
Loading
+101 −2
Original line number Diff line number Diff line
@@ -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 */

@@ -73,6 +74,24 @@
    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) */
}
+12 −0
Original line number Diff line number Diff line
@@ -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 */
+33 −70
Original line number Diff line number Diff line
@@ -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;
    value = ptr + 1;

      if(reset) {
        reset = FALSE;
        imapc->preftype = IMAP_TYPE_NONE;
        imapc->sasl.prefmech = SASL_AUTH_NONE;
      }

      while(*ptr && *ptr != ';') {
    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;
      }
    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++;
  }
    else
      result = CURLE_URL_MALFORMAT;

  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;
+1 −1
Original line number Diff line number Diff line
@@ -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
+37 −70
Original line number Diff line number Diff line
@@ -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,73 +1984,50 @@ 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 != ';') {
    while(*ptr && *ptr != ';')
      ptr++;
        len++;
      }

      if(strnequal(value, "*", len)) {
        pop3c->preftype = POP3_TYPE_ANY;
        pop3c->sasl.prefmech = SASL_AUTH_ANY;
      }
      else if(strnequal(value, "+APOP", len)) {
    if(strnequal(key, "AUTH=", 5)) {
      result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
                                               value, ptr - value);

      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;
    }
    else
      result = CURLE_URL_MALFORMAT;

    if(*ptr == ';')
      ptr++;
  }
    else
      result = CURLE_URL_MALFORMAT;

  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;
Loading