From db205177961218ebd8e358351dc9dd94490c79e6 Mon Sep 17 00:00:00 2001 From: Steve Holme Date: Sun, 6 Jan 2013 19:13:58 +0000 Subject: [PATCH] imap: Added support for SASL based authentication mechanism detection Added support for detecting the supported SASL authentication mechanisms via the CAPABILITY command. --- lib/imap.c | 120 +++++++++++++++++++++++++++++++++++++++++--- lib/imap.h | 16 +++--- tests/data/test1321 | 9 ++-- tests/data/test801 | 9 ++-- 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, , et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, , 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 @@ -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, , et al. + * Copyright (C) 2009 - 2013, Daniel Stenberg, , 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/.* -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 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" -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