aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Holme <steve_holme@hotmail.com>2013-01-06 19:13:58 +0000
committerSteve Holme <steve_holme@hotmail.com>2013-01-06 19:13:58 +0000
commitdb205177961218ebd8e358351dc9dd94490c79e6 (patch)
tree8a5e2313f13804ba32528e6b44428cf39c6fb8a7
parent4a5aa6682daeea7cd7cbbb47ce3e1c310e9261c2 (diff)
imap: Added support for SASL based authentication mechanism detection
Added support for detecting the supported SASL authentication mechanisms via the CAPABILITY command.
-rw-r--r--lib/imap.c120
-rw-r--r--lib/imap.h16
-rw-r--r--tests/data/test13219
-rw-r--r--tests/data/test8019
4 files changed, 132 insertions, 22 deletions
diff --git a/lib/imap.c b/lib/imap.c
index ab11464fb..388af0496 100644
--- a/lib/imap.c
+++ b/lib/imap.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, 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
@@ -70,6 +70,7 @@
#include "multiif.h"
#include "url.h"
#include "rawstr.h"
+#include "curl_sasl.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@@ -320,7 +321,8 @@ static char* imap_atom(const char* str)
}
/* Function that checks for an ending imap status code at the start of the
- given string. */
+ given string but also detects the supported mechanisms from the CAPABILITY
+ response. */
static int imap_endofresp(struct pingpong *pp, int *resp)
{
char *line = pp->linestart_resp;
@@ -328,6 +330,7 @@ static int imap_endofresp(struct pingpong *pp, int *resp)
struct imap_conn *imapc = &pp->conn->proto.imapc;
const char *id = imapc->idstr;
size_t id_len = strlen(id);
+ size_t wordlen;
/* Do we have a generic command response? */
if(len >= id_len + 3) {
@@ -337,6 +340,64 @@ static int imap_endofresp(struct pingpong *pp, int *resp)
}
}
+ /* Are we processing CAPABILITY command responses? */
+ if(imapc->state == IMAP_CAPABILITY) {
+ /* Do we have a valid response? */
+ if(len >= 2 && !memcmp("* ", line, 2)) {
+ line += 2;
+ len -= 2;
+
+ /* Loop through the data line */
+ for(;;) {
+ while(len &&
+ (*line == ' ' || *line == '\t' ||
+ *line == '\r' || *line == '\n')) {
+
+ if(*line == '\n')
+ return FALSE;
+
+ line++;
+ len--;
+ }
+
+ if(!len)
+ break;
+
+ /* Extract the word */
+ for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
+ line[wordlen] != '\t' && line[wordlen] != '\r' &&
+ line[wordlen] != '\n';)
+ wordlen++;
+
+ /* Do we have an AUTH capability? */
+ if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
+ line += 5;
+ len -= 5;
+ wordlen -= 5;
+
+ /* Test the word for a matching authentication mechanism */
+ if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
+ imapc->authmechs |= SASL_MECH_LOGIN;
+ if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
+ imapc->authmechs |= SASL_MECH_PLAIN;
+ else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
+ imapc->authmechs |= SASL_MECH_CRAM_MD5;
+ else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
+ imapc->authmechs |= SASL_MECH_DIGEST_MD5;
+ else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
+ imapc->authmechs |= SASL_MECH_GSSAPI;
+ else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
+ imapc->authmechs |= SASL_MECH_EXTERNAL;
+ else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
+ imapc->authmechs |= SASL_MECH_NTLM;
+ }
+
+ line += wordlen;
+ len -= wordlen;
+ }
+ }
+ }
+
/* Are we processing FETCH command responses? */
if(imapc->state == IMAP_FETCH) {
/* Do we have a valid response? */
@@ -346,7 +407,7 @@ static int imap_endofresp(struct pingpong *pp, int *resp)
}
}
- return FALSE; /* nothing for us */
+ return FALSE; /* Nothing for us */
}
/* This is the ONLY way to change IMAP state! */
@@ -361,6 +422,7 @@ static void state(struct connectdata *conn,
"SERVERGREET",
"STARTTLS",
"UPGRADETLS",
+ "CAPABILITY",
"LOGIN",
"SELECT",
"FETCH",
@@ -376,6 +438,35 @@ static void state(struct connectdata *conn,
imapc->state = newstate;
}
+static CURLcode imap_state_capability(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ const char *str;
+
+ imapc->authmechs = 0; /* No known authentication mechanisms yet */
+
+ /* Check we have a username and password to authenticate with and end the
+ connect phase if we don't */
+ if(!conn->bits.user_passwd) {
+ state(conn, IMAP_STOP);
+
+ return result;
+ }
+
+ str = getcmdid(conn);
+
+ /* Send the CAPABILITY command */
+ result = imap_sendf(conn, str, "%s CAPABILITY", str);
+
+ if(result)
+ return result;
+
+ state(conn, IMAP_CAPABILITY);
+
+ return CURLE_OK;
+}
+
static CURLcode imap_state_login(struct connectdata *conn)
{
CURLcode result;
@@ -439,7 +530,7 @@ static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
state(conn, IMAP_STARTTLS);
}
else
- result = imap_state_login(conn);
+ result = imap_state_capability(conn);
return result;
}
@@ -460,7 +551,7 @@ static CURLcode imap_state_starttls_resp(struct connectdata *conn,
result = CURLE_USE_SSL_FAILED;
}
else
- result = imap_state_login(conn);
+ result = imap_state_capability(conn);
}
else {
if(data->state.used_interface == Curl_if_multi) {
@@ -471,7 +562,7 @@ static CURLcode imap_state_starttls_resp(struct connectdata *conn,
result = Curl_ssl_connect(conn, FIRSTSOCKET);
if(CURLE_OK == result) {
imap_to_imaps(conn);
- result = imap_state_login(conn);
+ result = imap_state_capability(conn);
}
}
}
@@ -488,12 +579,23 @@ static CURLcode imap_state_upgrade_tls(struct connectdata *conn)
if(imapc->ssldone) {
imap_to_imaps(conn);
- result = imap_state_login(conn);
+ result = imap_state_capability(conn);
}
return result;
}
+/* For CAPABILITY responses */
+static CURLcode imap_state_capability_resp(struct connectdata *conn,
+ int imapcode,
+ imapstate instate)
+{
+ (void)imapcode; /* no use for this yet */
+ (void)instate; /* no use for this yet */
+
+ return imap_state_login(conn);
+}
+
/* For LOGIN responses */
static CURLcode imap_state_login_resp(struct connectdata *conn,
int imapcode,
@@ -694,6 +796,10 @@ static CURLcode imap_statemach_act(struct connectdata *conn)
result = imap_state_starttls_resp(conn, imapcode, imapc->state);
break;
+ case IMAP_CAPABILITY:
+ result = imap_state_capability_resp(conn, imapcode, imapc->state);
+ break;
+
case IMAP_LOGIN:
result = imap_state_login_resp(conn, imapcode, imapc->state);
break;
diff --git a/lib/imap.h b/lib/imap.h
index 4cd6bc76a..60cb0465a 100644
--- a/lib/imap.h
+++ b/lib/imap.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2009 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2009 - 2013, 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
@@ -34,6 +34,7 @@ typedef enum {
IMAP_STARTTLS,
IMAP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS
(multi mode only) */
+ IMAP_CAPABILITY,
IMAP_LOGIN,
IMAP_SELECT,
IMAP_FETCH,
@@ -45,12 +46,13 @@ typedef enum {
struct */
struct imap_conn {
struct pingpong pp;
- char *mailbox; /* Message ID to fetch */
- 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 */
- bool ssldone; /* Is connect() over SSL done? Only relevant in
- multi mode */
+ char *mailbox; /* Message ID to fetch */
+ unsigned int authmechs; /* Accepted authentication mechanisms */
+ 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 */
+ bool ssldone; /* Is connect() over SSL done? Only relevant in
+ multi mode */
};
extern const struct Curl_handler Curl_handler_imap;
diff --git a/tests/data/test1321 b/tests/data/test1321
index 7d45d9640..4a04575b1 100644
--- a/tests/data/test1321
+++ b/tests/data/test1321
@@ -56,10 +56,11 @@ imap://%HOSTIP:%IMAPPORT/1321 -u user:secret -p -x %HOSTIP:%PROXYPORT
^User-Agent: curl/.*
</strip>
<protocol>
-B LOGIN user secret
-C SELECT 1321
-D FETCH 1 BODY[TEXT]
-A LOGOUT
+B CAPABILITY
+C LOGIN user secret
+D SELECT 1321
+A FETCH 1 BODY[TEXT]
+B LOGOUT
</protocol>
<proxy>
CONNECT %HOSTIP:%IMAPPORT HTTP/1.1
diff --git a/tests/data/test801 b/tests/data/test801
index 271d8d1f7..d5f1cfeb9 100644
--- a/tests/data/test801
+++ b/tests/data/test801
@@ -38,10 +38,11 @@ imap://%HOSTIP:%IMAPPORT/801 -u user:secret
# Verify data after the test has been "shot"
<verify>
<protocol>
-B LOGIN user secret
-C SELECT 801
-D FETCH 1 BODY[TEXT]
-A LOGOUT
+B CAPABILITY
+C LOGIN user secret
+D SELECT 801
+A FETCH 1 BODY[TEXT]
+B LOGOUT
</protocol>
</verify>
</testcase>