1
0
mirror of https://github.com/moparisthebest/curl synced 2024-10-31 15:45:12 -04:00

sasl: implement EXTERNAL authentication mechanism.

Its use is only enabled by explicit requirement in URL (;AUTH=EXTERNAL) and
by not setting the password.
This commit is contained in:
Patrick Monnerat 2015-01-27 17:24:55 +01:00
parent e1bb13c09f
commit 0d24f64473
5 changed files with 135 additions and 75 deletions

View File

@ -368,6 +368,30 @@ static CURLcode sasl_create_login_message(struct SessionHandle *data,
return Curl_base64_encode(data, valuep, vlen, outptr, outlen); 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 #ifndef CURL_DISABLE_CRYPTO_AUTH
/* /*
* sasl_decode_cram_md5_message() * sasl_decode_cram_md5_message()
@ -1257,7 +1281,7 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
} }
if(strnequal(value, "*", len)) if(strnequal(value, "*", len))
sasl->prefmech = SASL_AUTH_ANY; sasl->prefmech = SASL_AUTH_DEFAULT;
else if((mechbit = Curl_sasl_decode_mech(value, len, &mechlen)) && else if((mechbit = Curl_sasl_decode_mech(value, len, &mechlen)) &&
mechlen == len) mechlen == len)
sasl->prefmech |= mechbit; 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->params = params; /* Set protocol dependent parameters */
sasl->state = SASL_STOP; /* Not yet running */ sasl->state = SASL_STOP; /* Not yet running */
sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */ 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->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */
sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */ sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */
sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */ sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */
@ -1299,6 +1323,7 @@ static void state(struct SASL *sasl,
"PLAIN", "PLAIN",
"LOGIN", "LOGIN",
"LOGIN_PASSWD", "LOGIN_PASSWD",
"EXTERNAL",
"CRAMMD5", "CRAMMD5",
"DIGESTMD5", "DIGESTMD5",
"DIGESTMD5_RESP", "DIGESTMD5_RESP",
@ -1321,6 +1346,23 @@ static void state(struct SASL *sasl,
sasl->state = newstate; 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() * 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 /* Calculate the supported authentication mechanism, by decreasing order of
* security, as well as the initial response where appropriate */ * security, as well as the initial response where appropriate */
#if defined(USE_KERBEROS5) if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) {
if(enabledmechs & SASL_MECH_GSSAPI) { mech = SASL_MECH_STRING_EXTERNAL;
sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */ state1 = SASL_EXTERNAL;
mech = SASL_MECH_STRING_GSSAPI; sasl->authused = SASL_MECH_EXTERNAL;
state1 = SASL_GSSAPI;
state2 = SASL_GSSAPI_TOKEN;
sasl->authused = SASL_MECH_GSSAPI;
if(force_ir || data->set.sasl_ir) if(force_ir || data->set.sasl_ir)
result = Curl_sasl_create_gssapi_user_message(data, conn->user, result = sasl_create_external_message(data, conn->user, &resp, &len);
conn->passwd,
sasl->params->service,
sasl->mutual_auth,
NULL, &conn->krb5,
&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 #endif
#ifndef CURL_DISABLE_CRYPTO_AUTH #ifndef CURL_DISABLE_CRYPTO_AUTH
if(enabledmechs & SASL_MECH_DIGEST_MD5) { if(enabledmechs & SASL_MECH_DIGEST_MD5) {
mech = SASL_MECH_STRING_DIGEST_MD5; mech = SASL_MECH_STRING_DIGEST_MD5;
state1 = SASL_DIGESTMD5; state1 = SASL_DIGESTMD5;
sasl->authused = SASL_MECH_DIGEST_MD5; sasl->authused = SASL_MECH_DIGEST_MD5;
} }
else if(enabledmechs & SASL_MECH_CRAM_MD5) { else if(enabledmechs & SASL_MECH_CRAM_MD5) {
mech = SASL_MECH_STRING_CRAM_MD5; mech = SASL_MECH_STRING_CRAM_MD5;
state1 = SASL_CRAMMD5; state1 = SASL_CRAMMD5;
sasl->authused = SASL_MECH_CRAM_MD5; sasl->authused = SASL_MECH_CRAM_MD5;
} }
else else
#endif #endif
#ifdef USE_NTLM #ifdef USE_NTLM
if(enabledmechs & SASL_MECH_NTLM) { if(enabledmechs & SASL_MECH_NTLM) {
mech = SASL_MECH_STRING_NTLM; mech = SASL_MECH_STRING_NTLM;
state1 = SASL_NTLM; state1 = SASL_NTLM;
state2 = SASL_NTLM_TYPE2MSG; state2 = SASL_NTLM_TYPE2MSG;
sasl->authused = SASL_MECH_NTLM; sasl->authused = SASL_MECH_NTLM;
if(force_ir || data->set.sasl_ir) if(force_ir || data->set.sasl_ir)
result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
&conn->ntlm, &resp, &len); &conn->ntlm, &resp, &len);
} }
else else
#endif #endif
if((((enabledmechs & SASL_MECH_XOAUTH2) && if((((enabledmechs & SASL_MECH_XOAUTH2) &&
sasl->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { sasl->prefmech != SASL_AUTH_DEFAULT)) || conn->xoauth2_bearer) {
mech = SASL_MECH_STRING_XOAUTH2; mech = SASL_MECH_STRING_XOAUTH2;
state1 = SASL_XOAUTH2; state1 = SASL_XOAUTH2;
sasl->authused = SASL_MECH_XOAUTH2; sasl->authused = SASL_MECH_XOAUTH2;
if(force_ir || data->set.sasl_ir) if(force_ir || data->set.sasl_ir)
result = sasl_create_xoauth2_message(data, conn->user, result = sasl_create_xoauth2_message(data, conn->user,
conn->xoauth2_bearer, &resp, &len); conn->xoauth2_bearer,
} &resp, &len);
else if(enabledmechs & SASL_MECH_LOGIN) { }
mech = SASL_MECH_STRING_LOGIN; else if(enabledmechs & SASL_MECH_LOGIN) {
state1 = SASL_LOGIN; mech = SASL_MECH_STRING_LOGIN;
state2 = SASL_LOGIN_PASSWD; state1 = SASL_LOGIN;
sasl->authused = SASL_MECH_LOGIN; state2 = SASL_LOGIN_PASSWD;
sasl->authused = SASL_MECH_LOGIN;
if(force_ir || data->set.sasl_ir) if(force_ir || data->set.sasl_ir)
result = sasl_create_login_message(data, conn->user, &resp, &len); result = sasl_create_login_message(data, conn->user, &resp, &len);
} }
else if(enabledmechs & SASL_MECH_PLAIN) { else if(enabledmechs & SASL_MECH_PLAIN) {
mech = SASL_MECH_STRING_PLAIN; mech = SASL_MECH_STRING_PLAIN;
state1 = SASL_PLAIN; state1 = SASL_PLAIN;
sasl->authused = SASL_MECH_PLAIN; sasl->authused = SASL_MECH_PLAIN;
if(force_ir || data->set.sasl_ir) if(force_ir || data->set.sasl_ir)
result = sasl_create_plain_message(data, conn->user, conn->passwd, result = sasl_create_plain_message(data, conn->user, conn->passwd,
&resp, &len); &resp, &len);
}
} }
else
state2 = SASL_STOP; /* No authentication started */
if(!result) { if(!result) {
if(resp && sasl->params->maxirlen && if(resp && sasl->params->maxirlen &&
@ -1490,6 +1541,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
case SASL_LOGIN_PASSWD: case SASL_LOGIN_PASSWD:
result = sasl_create_login_message(data, conn->passwd, &resp, &len); result = sasl_create_login_message(data, conn->passwd, &resp, &len);
break; break;
case SASL_EXTERNAL:
result = sasl_create_external_message(data, conn->user, &resp, &len);
break;
#ifndef CURL_DISABLE_CRYPTO_AUTH #ifndef CURL_DISABLE_CRYPTO_AUTH
case SASL_CRAMMD5: case SASL_CRAMMD5:

View File

@ -39,10 +39,6 @@ struct ntlmdata;
struct kerberos5data; struct kerberos5data;
#endif #endif
/* Authentication mechanism values */
#define SASL_AUTH_NONE 0
#define SASL_AUTH_ANY ~0U
/* Authentication mechanism flags */ /* Authentication mechanism flags */
#define SASL_MECH_LOGIN (1 << 0) #define SASL_MECH_LOGIN (1 << 0)
#define SASL_MECH_PLAIN (1 << 1) #define SASL_MECH_PLAIN (1 << 1)
@ -53,6 +49,11 @@ struct kerberos5data;
#define SASL_MECH_NTLM (1 << 6) #define SASL_MECH_NTLM (1 << 6)
#define SASL_MECH_XOAUTH2 (1 << 7) #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 */ /* Authentication mechanism strings */
#define SASL_MECH_STRING_LOGIN "LOGIN" #define SASL_MECH_STRING_LOGIN "LOGIN"
#define SASL_MECH_STRING_PLAIN "PLAIN" #define SASL_MECH_STRING_PLAIN "PLAIN"
@ -74,6 +75,7 @@ typedef enum {
SASL_PLAIN, SASL_PLAIN,
SASL_LOGIN, SASL_LOGIN,
SASL_LOGIN_PASSWD, SASL_LOGIN_PASSWD,
SASL_EXTERNAL,
SASL_CRAMMD5, SASL_CRAMMD5,
SASL_DIGESTMD5, SASL_DIGESTMD5,
SASL_DIGESTMD5_RESP, SASL_DIGESTMD5_RESP,
@ -228,6 +230,9 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
/* Initializes an SASL structure */ /* Initializes an SASL structure */
void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params); 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 */ /* Calculate the required login details for SASL authentication */
CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
bool force_ir, saslprogress *progress); bool force_ir, saslprogress *progress);

View File

@ -612,9 +612,9 @@ static CURLcode imap_perform_authentication(struct connectdata *conn)
struct imap_conn *imapc = &conn->proto.imapc; struct imap_conn *imapc = &conn->proto.imapc;
saslprogress progress; 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 */ connect phase if we don't */
if(!conn->bits.user_passwd) { if(!Curl_sasl_can_authenticate(&imapc->sasl, conn)) {
state(conn, IMAP_STOP); state(conn, IMAP_STOP);
return result; return result;
} }
@ -1962,7 +1962,7 @@ static CURLcode imap_parse_url_options(struct connectdata *conn)
case SASL_AUTH_NONE: case SASL_AUTH_NONE:
imapc->preftype = IMAP_TYPE_NONE; imapc->preftype = IMAP_TYPE_NONE;
break; break;
case SASL_AUTH_ANY: case SASL_AUTH_DEFAULT:
imapc->preftype = IMAP_TYPE_ANY; imapc->preftype = IMAP_TYPE_ANY;
break; break;
default: default:

View File

@ -543,9 +543,9 @@ static CURLcode pop3_perform_authentication(struct connectdata *conn)
struct pop3_conn *pop3c = &conn->proto.pop3c; struct pop3_conn *pop3c = &conn->proto.pop3c;
saslprogress progress = SASL_IDLE; 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 */ connect phase if we don't */
if(!conn->bits.user_passwd) { if(!Curl_sasl_can_authenticate(&pop3c->sasl, conn)) {
state(conn, POP3_STOP); state(conn, POP3_STOP);
return result; return result;
} }
@ -1425,7 +1425,7 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn)
case SASL_AUTH_NONE: case SASL_AUTH_NONE:
pop3c->preftype = POP3_TYPE_NONE; pop3c->preftype = POP3_TYPE_NONE;
break; break;
case SASL_AUTH_ANY: case SASL_AUTH_DEFAULT:
pop3c->preftype = POP3_TYPE_ANY; pop3c->preftype = POP3_TYPE_ANY;
break; break;
default: default:

View File

@ -486,9 +486,10 @@ static CURLcode smtp_perform_authentication(struct connectdata *conn)
struct smtp_conn *smtpc = &conn->proto.smtpc; struct smtp_conn *smtpc = &conn->proto.smtpc;
saslprogress progress; 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 */ 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); state(conn, SMTP_STOP);
return result; return result;
} }