From 0d24f644735924524bcffff75ace4bd7b7a2e05a Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Tue, 27 Jan 2015 17:24:55 +0100 Subject: [PATCH] sasl: implement EXTERNAL authentication mechanism. Its use is only enabled by explicit requirement in URL (;AUTH=EXTERNAL) and by not setting the password. --- lib/curl_sasl.c | 188 +++++++++++++++++++++++++++++++----------------- lib/curl_sasl.h | 13 ++-- lib/imap.c | 6 +- lib/pop3.c | 6 +- lib/smtp.c | 5 +- 5 files changed, 139 insertions(+), 79 deletions(-) diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 347f1a30268e3f..2d55836cd3eacc 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -368,6 +368,30 @@ static CURLcode sasl_create_login_message(struct SessionHandle *data, return Curl_base64_encode(data, valuep, vlen, outptr, outlen); } +/* + * sasl_create_external_message() + * + * This is used to generate an already encoded EXTERNAL message containing + * the user name ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * user [in] - The user name. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +static CURLcode sasl_create_external_message(struct SessionHandle *data, + const char *user, char **outptr, + size_t *outlen) +{ + /* This is the same formatting as the login message. */ + return sasl_create_login_message(data, user, outptr, outlen); +} + #ifndef CURL_DISABLE_CRYPTO_AUTH /* * sasl_decode_cram_md5_message() @@ -1257,7 +1281,7 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, } if(strnequal(value, "*", len)) - sasl->prefmech = SASL_AUTH_ANY; + sasl->prefmech = SASL_AUTH_DEFAULT; else if((mechbit = Curl_sasl_decode_mech(value, len, &mechlen)) && mechlen == len) sasl->prefmech |= mechbit; @@ -1277,7 +1301,7 @@ void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params) sasl->params = params; /* Set protocol dependent parameters */ sasl->state = SASL_STOP; /* Not yet running */ sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */ - sasl->prefmech = SASL_AUTH_ANY; /* Prefer all mechanisms */ + sasl->prefmech = SASL_AUTH_DEFAULT; /* 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) */ @@ -1299,6 +1323,7 @@ static void state(struct SASL *sasl, "PLAIN", "LOGIN", "LOGIN_PASSWD", + "EXTERNAL", "CRAMMD5", "DIGESTMD5", "DIGESTMD5_RESP", @@ -1321,6 +1346,23 @@ static void state(struct SASL *sasl, sasl->state = newstate; } +/* + * Curl_sasl_can_authenticate() + * + * Check if we have enough auth data and capabilities to authenticate. + */ + +bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn) +{ + if(conn->bits.user_passwd) + return TRUE; /* Credentials provided */ + + if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL) + return TRUE; /* Can authenticate without password */ + + return FALSE; +} + /* * Curl_sasl_start() * @@ -1345,80 +1387,89 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, /* Calculate the supported authentication mechanism, by decreasing order of * security, as well as the initial response where appropriate */ -#if defined(USE_KERBEROS5) - if(enabledmechs & SASL_MECH_GSSAPI) { - sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */ - mech = SASL_MECH_STRING_GSSAPI; - state1 = SASL_GSSAPI; - state2 = SASL_GSSAPI_TOKEN; - sasl->authused = SASL_MECH_GSSAPI; + if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) { + mech = SASL_MECH_STRING_EXTERNAL; + state1 = SASL_EXTERNAL; + sasl->authused = SASL_MECH_EXTERNAL; if(force_ir || data->set.sasl_ir) - result = Curl_sasl_create_gssapi_user_message(data, conn->user, - conn->passwd, - sasl->params->service, - sasl->mutual_auth, - NULL, &conn->krb5, - &resp, &len); + result = sasl_create_external_message(data, conn->user, &resp, &len); } - else + else if(conn->bits.user_passwd) { +#if defined(USE_KERBEROS5) + if(enabledmechs & SASL_MECH_GSSAPI) { + sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */ + mech = SASL_MECH_STRING_GSSAPI; + state1 = SASL_GSSAPI; + state2 = SASL_GSSAPI_TOKEN; + sasl->authused = SASL_MECH_GSSAPI; + + if(force_ir || data->set.sasl_ir) + result = Curl_sasl_create_gssapi_user_message(data, conn->user, + conn->passwd, + sasl->params->service, + sasl->mutual_auth, + NULL, &conn->krb5, + &resp, &len); + } + else #endif #ifndef CURL_DISABLE_CRYPTO_AUTH - if(enabledmechs & SASL_MECH_DIGEST_MD5) { - mech = SASL_MECH_STRING_DIGEST_MD5; - state1 = SASL_DIGESTMD5; - sasl->authused = SASL_MECH_DIGEST_MD5; - } - else if(enabledmechs & SASL_MECH_CRAM_MD5) { - mech = SASL_MECH_STRING_CRAM_MD5; - state1 = SASL_CRAMMD5; - sasl->authused = SASL_MECH_CRAM_MD5; - } - else + if(enabledmechs & SASL_MECH_DIGEST_MD5) { + mech = SASL_MECH_STRING_DIGEST_MD5; + state1 = SASL_DIGESTMD5; + sasl->authused = SASL_MECH_DIGEST_MD5; + } + else if(enabledmechs & SASL_MECH_CRAM_MD5) { + mech = SASL_MECH_STRING_CRAM_MD5; + state1 = SASL_CRAMMD5; + sasl->authused = SASL_MECH_CRAM_MD5; + } + else #endif #ifdef USE_NTLM - if(enabledmechs & SASL_MECH_NTLM) { - mech = SASL_MECH_STRING_NTLM; - state1 = SASL_NTLM; - state2 = SASL_NTLM_TYPE2MSG; - sasl->authused = SASL_MECH_NTLM; - - if(force_ir || data->set.sasl_ir) - result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, - &conn->ntlm, &resp, &len); - } - else + if(enabledmechs & SASL_MECH_NTLM) { + mech = SASL_MECH_STRING_NTLM; + state1 = SASL_NTLM; + state2 = SASL_NTLM_TYPE2MSG; + sasl->authused = SASL_MECH_NTLM; + + if(force_ir || data->set.sasl_ir) + result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, + &conn->ntlm, &resp, &len); + } + else #endif - if((((enabledmechs & SASL_MECH_XOAUTH2) && - sasl->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { - mech = SASL_MECH_STRING_XOAUTH2; - state1 = SASL_XOAUTH2; - sasl->authused = SASL_MECH_XOAUTH2; - - if(force_ir || data->set.sasl_ir) - result = sasl_create_xoauth2_message(data, conn->user, - conn->xoauth2_bearer, &resp, &len); - } - else if(enabledmechs & SASL_MECH_LOGIN) { - mech = SASL_MECH_STRING_LOGIN; - state1 = SASL_LOGIN; - state2 = SASL_LOGIN_PASSWD; - sasl->authused = SASL_MECH_LOGIN; - - if(force_ir || data->set.sasl_ir) - result = sasl_create_login_message(data, conn->user, &resp, &len); - } - else if(enabledmechs & SASL_MECH_PLAIN) { - mech = SASL_MECH_STRING_PLAIN; - state1 = SASL_PLAIN; - sasl->authused = SASL_MECH_PLAIN; - - if(force_ir || data->set.sasl_ir) - result = sasl_create_plain_message(data, conn->user, conn->passwd, - &resp, &len); + if((((enabledmechs & SASL_MECH_XOAUTH2) && + sasl->prefmech != SASL_AUTH_DEFAULT)) || conn->xoauth2_bearer) { + mech = SASL_MECH_STRING_XOAUTH2; + state1 = SASL_XOAUTH2; + sasl->authused = SASL_MECH_XOAUTH2; + + if(force_ir || data->set.sasl_ir) + result = sasl_create_xoauth2_message(data, conn->user, + conn->xoauth2_bearer, + &resp, &len); + } + else if(enabledmechs & SASL_MECH_LOGIN) { + mech = SASL_MECH_STRING_LOGIN; + state1 = SASL_LOGIN; + state2 = SASL_LOGIN_PASSWD; + sasl->authused = SASL_MECH_LOGIN; + + if(force_ir || data->set.sasl_ir) + result = sasl_create_login_message(data, conn->user, &resp, &len); + } + else if(enabledmechs & SASL_MECH_PLAIN) { + mech = SASL_MECH_STRING_PLAIN; + state1 = SASL_PLAIN; + sasl->authused = SASL_MECH_PLAIN; + + if(force_ir || data->set.sasl_ir) + result = sasl_create_plain_message(data, conn->user, conn->passwd, + &resp, &len); + } } - else - state2 = SASL_STOP; /* No authentication started */ if(!result) { if(resp && sasl->params->maxirlen && @@ -1490,6 +1541,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, case SASL_LOGIN_PASSWD: result = sasl_create_login_message(data, conn->passwd, &resp, &len); break; + case SASL_EXTERNAL: + result = sasl_create_external_message(data, conn->user, &resp, &len); + break; #ifndef CURL_DISABLE_CRYPTO_AUTH case SASL_CRAMMD5: diff --git a/lib/curl_sasl.h b/lib/curl_sasl.h index e4a594c5385d64..985ca416ee1b32 100644 --- a/lib/curl_sasl.h +++ b/lib/curl_sasl.h @@ -39,10 +39,6 @@ struct ntlmdata; struct kerberos5data; #endif -/* Authentication mechanism values */ -#define SASL_AUTH_NONE 0 -#define SASL_AUTH_ANY ~0U - /* Authentication mechanism flags */ #define SASL_MECH_LOGIN (1 << 0) #define SASL_MECH_PLAIN (1 << 1) @@ -53,6 +49,11 @@ struct kerberos5data; #define SASL_MECH_NTLM (1 << 6) #define SASL_MECH_XOAUTH2 (1 << 7) +/* Authentication mechanism values */ +#define SASL_AUTH_NONE 0 +#define SASL_AUTH_ANY ~0U +#define SASL_AUTH_DEFAULT (SASL_AUTH_ANY & ~SASL_MECH_EXTERNAL) + /* Authentication mechanism strings */ #define SASL_MECH_STRING_LOGIN "LOGIN" #define SASL_MECH_STRING_PLAIN "PLAIN" @@ -74,6 +75,7 @@ typedef enum { SASL_PLAIN, SASL_LOGIN, SASL_LOGIN_PASSWD, + SASL_EXTERNAL, SASL_CRAMMD5, SASL_DIGESTMD5, SASL_DIGESTMD5_RESP, @@ -228,6 +230,9 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, /* Initializes an SASL structure */ void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params); +/* Check if we have enough auth data and capabilities to authenticate */ +bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn); + /* Calculate the required login details for SASL authentication */ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, bool force_ir, saslprogress *progress); diff --git a/lib/imap.c b/lib/imap.c index c5c8e4aeafff7b..2d037eeb3524d1 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -612,9 +612,9 @@ static CURLcode imap_perform_authentication(struct connectdata *conn) struct imap_conn *imapc = &conn->proto.imapc; saslprogress progress; - /* Check we have a username and password to authenticate with and end the + /* Check we have enough data to authenticate with and end the connect phase if we don't */ - if(!conn->bits.user_passwd) { + if(!Curl_sasl_can_authenticate(&imapc->sasl, conn)) { state(conn, IMAP_STOP); return result; } @@ -1962,7 +1962,7 @@ static CURLcode imap_parse_url_options(struct connectdata *conn) case SASL_AUTH_NONE: imapc->preftype = IMAP_TYPE_NONE; break; - case SASL_AUTH_ANY: + case SASL_AUTH_DEFAULT: imapc->preftype = IMAP_TYPE_ANY; break; default: diff --git a/lib/pop3.c b/lib/pop3.c index a7213a44edd343..d3b59f2490ee5c 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -543,9 +543,9 @@ static CURLcode pop3_perform_authentication(struct connectdata *conn) struct pop3_conn *pop3c = &conn->proto.pop3c; saslprogress progress = SASL_IDLE; - /* Check we have a username and password to authenticate with and end the + /* Check we have enough data to authenticate with and end the connect phase if we don't */ - if(!conn->bits.user_passwd) { + if(!Curl_sasl_can_authenticate(&pop3c->sasl, conn)) { state(conn, POP3_STOP); return result; } @@ -1425,7 +1425,7 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn) case SASL_AUTH_NONE: pop3c->preftype = POP3_TYPE_NONE; break; - case SASL_AUTH_ANY: + case SASL_AUTH_DEFAULT: pop3c->preftype = POP3_TYPE_ANY; break; default: diff --git a/lib/smtp.c b/lib/smtp.c index ca1ab2cece66e2..550725a93edd33 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -486,9 +486,10 @@ static CURLcode smtp_perform_authentication(struct connectdata *conn) struct smtp_conn *smtpc = &conn->proto.smtpc; saslprogress progress; - /* Check we have a username and password to authenticate with, and the + /* Check we have enough data to authenticate with, and the server supports authentiation, and end the connect phase if not */ - if(!conn->bits.user_passwd || !smtpc->auth_supported) { + if(!smtpc->auth_supported || + !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) { state(conn, SMTP_STOP); return result; }