mirror of
https://github.com/curl/curl.git
synced 2024-12-21 06:50:10 +08:00
af22c2a546
- almost all backend calls pass the Curl_cfilter intance instead of connectdata+sockindex - ssl_connect_data is remove from struct connectdata and made internal to vtls - ssl_connect_data is allocated in the added filter, kept at cf->ctx - added function to let a ssl filter access its ssl_primary_config and ssl_config_data this selects the propert subfields in conn and data, for filters added as plain or proxy - adjusted all backends to use the changed api - adjusted all backends to access config data via the exposed functions, no longer using conn or data directly cfilter renames for clear purpose: - methods `Curl_conn_*(data, conn, sockindex)` work on the complete filter chain at `sockindex` and connection `conn`. - methods `Curl_cf_*(cf, ...)` work on a specific Curl_cfilter instance. - methods `Curl_conn_cf()` work on/with filter instances at a connection. - rebased and resolved some naming conflicts - hostname validation (und session lookup) on SECONDARY use the same name as on FIRST (again). new debug macros and removing connectdata from function signatures where not needed. adapting schannel for new Curl_read_plain paramter. Closes #9919
752 lines
24 KiB
C
752 lines
24 KiB
C
/***************************************************************************
|
|
* _ _ ____ _
|
|
* Project ___| | | | _ \| |
|
|
* / __| | | | |_) | |
|
|
* | (__| |_| | _ <| |___
|
|
* \___|\___/|_| \_\_____|
|
|
*
|
|
* Copyright (C) 2012 - 2022, 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
|
|
* are also available at https://curl.se/docs/copyright.html.
|
|
*
|
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
|
* copies of the Software, and permit persons to whom the Software is
|
|
* furnished to do so, under the terms of the COPYING file.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
* SPDX-License-Identifier: curl
|
|
*
|
|
* RFC2195 CRAM-MD5 authentication
|
|
* RFC2617 Basic and Digest Access Authentication
|
|
* RFC2831 DIGEST-MD5 authentication
|
|
* RFC4422 Simple Authentication and Security Layer (SASL)
|
|
* RFC4616 PLAIN authentication
|
|
* RFC5802 SCRAM-SHA-1 authentication
|
|
* RFC7677 SCRAM-SHA-256 authentication
|
|
* RFC6749 OAuth 2.0 Authorization Framework
|
|
* RFC7628 A Set of SASL Mechanisms for OAuth
|
|
* Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "curl_setup.h"
|
|
|
|
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
|
|
!defined(CURL_DISABLE_POP3)
|
|
|
|
#include <curl/curl.h>
|
|
#include "urldata.h"
|
|
|
|
#include "curl_base64.h"
|
|
#include "curl_md5.h"
|
|
#include "vauth/vauth.h"
|
|
#include "cfilters.h"
|
|
#include "vtls/vtls.h"
|
|
#include "curl_hmac.h"
|
|
#include "curl_sasl.h"
|
|
#include "warnless.h"
|
|
#include "strtok.h"
|
|
#include "sendf.h"
|
|
/* The last 3 #include files should be in this order */
|
|
#include "curl_printf.h"
|
|
#include "curl_memory.h"
|
|
#include "memdebug.h"
|
|
|
|
/* Supported mechanisms */
|
|
static const struct {
|
|
const char *name; /* Name */
|
|
size_t len; /* Name length */
|
|
unsigned short 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 },
|
|
{ "OAUTHBEARER", 11, SASL_MECH_OAUTHBEARER },
|
|
{ "SCRAM-SHA-1", 11, SASL_MECH_SCRAM_SHA_1 },
|
|
{ "SCRAM-SHA-256",13, SASL_MECH_SCRAM_SHA_256 },
|
|
{ ZERO_NULL, 0, 0 }
|
|
};
|
|
|
|
/*
|
|
* Curl_sasl_cleanup()
|
|
*
|
|
* This is used to cleanup any libraries or curl modules used by the sasl
|
|
* functions.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* conn [in] - The connection data.
|
|
* authused [in] - The authentication mechanism used.
|
|
*/
|
|
void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused)
|
|
{
|
|
(void)conn;
|
|
(void)authused;
|
|
|
|
#if defined(USE_KERBEROS5)
|
|
/* Cleanup the gssapi structure */
|
|
if(authused == SASL_MECH_GSSAPI) {
|
|
Curl_auth_cleanup_gssapi(&conn->krb5);
|
|
}
|
|
#endif
|
|
|
|
#if defined(USE_GSASL)
|
|
/* Cleanup the GSASL structure */
|
|
if(authused & (SASL_MECH_SCRAM_SHA_1 | SASL_MECH_SCRAM_SHA_256)) {
|
|
Curl_auth_gsasl_cleanup(&conn->gsasl);
|
|
}
|
|
#endif
|
|
|
|
#if defined(USE_NTLM)
|
|
/* Cleanup the NTLM structure */
|
|
if(authused == SASL_MECH_NTLM) {
|
|
Curl_auth_cleanup_ntlm(&conn->ntlm);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Curl_sasl_decode_mech()
|
|
*
|
|
* Convert a SASL mechanism name into a token.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* ptr [in] - The mechanism string.
|
|
* maxlen [in] - Maximum mechanism string length.
|
|
* len [out] - If not NULL, effective name length.
|
|
*
|
|
* Returns the SASL mechanism token or 0 if no match.
|
|
*/
|
|
unsigned short 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;
|
|
size_t mechlen;
|
|
|
|
if(!len)
|
|
return CURLE_URL_MALFORMAT;
|
|
|
|
if(sasl->resetprefs) {
|
|
sasl->resetprefs = FALSE;
|
|
sasl->prefmech = SASL_AUTH_NONE;
|
|
}
|
|
|
|
if(!strncmp(value, "*", len))
|
|
sasl->prefmech = SASL_AUTH_DEFAULT;
|
|
else {
|
|
unsigned short mechbit = Curl_sasl_decode_mech(value, len, &mechlen);
|
|
if(mechbit && mechlen == len)
|
|
sasl->prefmech |= mechbit;
|
|
else
|
|
result = CURLE_URL_MALFORMAT;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Curl_sasl_init()
|
|
*
|
|
* Initializes the SASL structure.
|
|
*/
|
|
void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data,
|
|
const struct SASLproto *params)
|
|
{
|
|
unsigned long auth = data->set.httpauth;
|
|
|
|
sasl->params = params; /* Set protocol dependent parameters */
|
|
sasl->state = SASL_STOP; /* Not yet running */
|
|
sasl->curmech = NULL; /* No mechanism yet. */
|
|
sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */
|
|
sasl->prefmech = params->defmechs; /* Default preferred mechanisms */
|
|
sasl->authused = SASL_AUTH_NONE; /* The authentication mechanism used */
|
|
sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */
|
|
sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */
|
|
sasl->force_ir = FALSE; /* Respect external option */
|
|
|
|
if(auth != CURLAUTH_BASIC) {
|
|
sasl->resetprefs = FALSE;
|
|
sasl->prefmech = SASL_AUTH_NONE;
|
|
if(auth & CURLAUTH_BASIC)
|
|
sasl->prefmech |= SASL_MECH_PLAIN | SASL_MECH_LOGIN;
|
|
if(auth & CURLAUTH_DIGEST)
|
|
sasl->prefmech |= SASL_MECH_DIGEST_MD5;
|
|
if(auth & CURLAUTH_NTLM)
|
|
sasl->prefmech |= SASL_MECH_NTLM;
|
|
if(auth & CURLAUTH_BEARER)
|
|
sasl->prefmech |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2;
|
|
if(auth & CURLAUTH_GSSAPI)
|
|
sasl->prefmech |= SASL_MECH_GSSAPI;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* state()
|
|
*
|
|
* This is the ONLY way to change SASL state!
|
|
*/
|
|
static void state(struct SASL *sasl, struct Curl_easy *data,
|
|
saslstate newstate)
|
|
{
|
|
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
|
|
/* for debug purposes */
|
|
static const char * const names[]={
|
|
"STOP",
|
|
"PLAIN",
|
|
"LOGIN",
|
|
"LOGIN_PASSWD",
|
|
"EXTERNAL",
|
|
"CRAMMD5",
|
|
"DIGESTMD5",
|
|
"DIGESTMD5_RESP",
|
|
"NTLM",
|
|
"NTLM_TYPE2MSG",
|
|
"GSSAPI",
|
|
"GSSAPI_TOKEN",
|
|
"GSSAPI_NO_DATA",
|
|
"OAUTH2",
|
|
"OAUTH2_RESP",
|
|
"GSASL",
|
|
"CANCEL",
|
|
"FINAL",
|
|
/* LAST */
|
|
};
|
|
|
|
if(sasl->state != newstate)
|
|
infof(data, "SASL %p state change from %s to %s",
|
|
(void *)sasl, names[sasl->state], names[newstate]);
|
|
#else
|
|
(void) data;
|
|
#endif
|
|
|
|
sasl->state = newstate;
|
|
}
|
|
|
|
/* Get the SASL server message and convert it to binary. */
|
|
static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data,
|
|
struct bufref *out)
|
|
{
|
|
CURLcode result = CURLE_OK;
|
|
|
|
result = sasl->params->getmessage(data, out);
|
|
if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) {
|
|
unsigned char *msg;
|
|
size_t msglen;
|
|
const char *serverdata = (const char *) Curl_bufref_ptr(out);
|
|
|
|
if(!*serverdata || *serverdata == '=')
|
|
Curl_bufref_set(out, NULL, 0, NULL);
|
|
else {
|
|
result = Curl_base64_decode(serverdata, &msg, &msglen);
|
|
if(!result)
|
|
Curl_bufref_set(out, msg, msglen, curl_free);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Encode the outgoing SASL message. */
|
|
static CURLcode build_message(struct SASL *sasl, struct bufref *msg)
|
|
{
|
|
CURLcode result = CURLE_OK;
|
|
|
|
if(sasl->params->flags & SASL_FLAG_BASE64) {
|
|
if(!Curl_bufref_ptr(msg)) /* Empty message. */
|
|
Curl_bufref_set(msg, "", 0, NULL);
|
|
else if(!Curl_bufref_len(msg)) /* Explicit empty response. */
|
|
Curl_bufref_set(msg, "=", 1, NULL);
|
|
else {
|
|
char *base64;
|
|
size_t base64len;
|
|
|
|
result = Curl_base64_encode((const char *) Curl_bufref_ptr(msg),
|
|
Curl_bufref_len(msg), &base64, &base64len);
|
|
if(!result)
|
|
Curl_bufref_set(msg, base64, base64len, curl_free);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Curl_sasl_can_authenticate()
|
|
*
|
|
* Check if we have enough auth data and capabilities to authenticate.
|
|
*/
|
|
bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data)
|
|
{
|
|
/* Have credentials been provided? */
|
|
if(data->state.aptr.user)
|
|
return TRUE;
|
|
|
|
/* EXTERNAL can authenticate without a user name and/or password */
|
|
if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Curl_sasl_start()
|
|
*
|
|
* Calculate the required login details for SASL authentication.
|
|
*/
|
|
CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
|
|
bool force_ir, saslprogress *progress)
|
|
{
|
|
CURLcode result = CURLE_OK;
|
|
struct connectdata *conn = data->conn;
|
|
unsigned short enabledmechs;
|
|
const char *mech = NULL;
|
|
struct bufref resp;
|
|
saslstate state1 = SASL_STOP;
|
|
saslstate state2 = SASL_FINAL;
|
|
const char *hostname, *disp_hostname;
|
|
int port;
|
|
#if defined(USE_KERBEROS5) || defined(USE_NTLM)
|
|
const char *service = data->set.str[STRING_SERVICE_NAME] ?
|
|
data->set.str[STRING_SERVICE_NAME] :
|
|
sasl->params->service;
|
|
#endif
|
|
const char *oauth_bearer = data->set.str[STRING_BEARER];
|
|
struct bufref nullmsg;
|
|
|
|
Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
|
|
Curl_bufref_init(&nullmsg);
|
|
Curl_bufref_init(&resp);
|
|
sasl->force_ir = force_ir; /* Latch for future use */
|
|
sasl->authused = 0; /* No mechanism used yet */
|
|
enabledmechs = sasl->authmechs & sasl->prefmech;
|
|
*progress = SASL_IDLE;
|
|
|
|
/* Calculate the supported authentication mechanism, by decreasing order of
|
|
security, as well as the initial response where appropriate */
|
|
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_auth_create_external_message(conn->user, &resp);
|
|
}
|
|
else if(data->state.aptr.user) {
|
|
#if defined(USE_KERBEROS5)
|
|
if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() &&
|
|
Curl_auth_user_contains_domain(conn->user)) {
|
|
sasl->mutual_auth = FALSE;
|
|
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_auth_create_gssapi_user_message(data, conn->user,
|
|
conn->passwd,
|
|
service,
|
|
conn->host.name,
|
|
sasl->mutual_auth,
|
|
NULL, &conn->krb5,
|
|
&resp);
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef USE_GSASL
|
|
if((enabledmechs & SASL_MECH_SCRAM_SHA_256) &&
|
|
Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256,
|
|
&conn->gsasl)) {
|
|
mech = SASL_MECH_STRING_SCRAM_SHA_256;
|
|
sasl->authused = SASL_MECH_SCRAM_SHA_256;
|
|
state1 = SASL_GSASL;
|
|
state2 = SASL_GSASL;
|
|
|
|
result = Curl_auth_gsasl_start(data, conn->user,
|
|
conn->passwd, &conn->gsasl);
|
|
if(result == CURLE_OK && (force_ir || data->set.sasl_ir))
|
|
result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp);
|
|
}
|
|
else if((enabledmechs & SASL_MECH_SCRAM_SHA_1) &&
|
|
Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1,
|
|
&conn->gsasl)) {
|
|
mech = SASL_MECH_STRING_SCRAM_SHA_1;
|
|
sasl->authused = SASL_MECH_SCRAM_SHA_1;
|
|
state1 = SASL_GSASL;
|
|
state2 = SASL_GSASL;
|
|
|
|
result = Curl_auth_gsasl_start(data, conn->user,
|
|
conn->passwd, &conn->gsasl);
|
|
if(result == CURLE_OK && (force_ir || data->set.sasl_ir))
|
|
result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp);
|
|
}
|
|
else
|
|
#endif
|
|
#ifndef CURL_DISABLE_CRYPTO_AUTH
|
|
if((enabledmechs & SASL_MECH_DIGEST_MD5) &&
|
|
Curl_auth_is_digest_supported()) {
|
|
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) && Curl_auth_is_ntlm_supported()) {
|
|
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_auth_create_ntlm_type1_message(data,
|
|
conn->user, conn->passwd,
|
|
service,
|
|
hostname,
|
|
&conn->ntlm, &resp);
|
|
}
|
|
else
|
|
#endif
|
|
if((enabledmechs & SASL_MECH_OAUTHBEARER) && oauth_bearer) {
|
|
mech = SASL_MECH_STRING_OAUTHBEARER;
|
|
state1 = SASL_OAUTH2;
|
|
state2 = SASL_OAUTH2_RESP;
|
|
sasl->authused = SASL_MECH_OAUTHBEARER;
|
|
|
|
if(force_ir || data->set.sasl_ir)
|
|
result = Curl_auth_create_oauth_bearer_message(conn->user,
|
|
hostname,
|
|
port,
|
|
oauth_bearer,
|
|
&resp);
|
|
}
|
|
else if((enabledmechs & SASL_MECH_XOAUTH2) && oauth_bearer) {
|
|
mech = SASL_MECH_STRING_XOAUTH2;
|
|
state1 = SASL_OAUTH2;
|
|
sasl->authused = SASL_MECH_XOAUTH2;
|
|
|
|
if(force_ir || data->set.sasl_ir)
|
|
result = Curl_auth_create_xoauth_bearer_message(conn->user,
|
|
oauth_bearer,
|
|
&resp);
|
|
}
|
|
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 = Curl_auth_create_plain_message(conn->sasl_authzid,
|
|
conn->user, conn->passwd,
|
|
&resp);
|
|
}
|
|
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 = Curl_auth_create_login_message(conn->user, &resp);
|
|
}
|
|
}
|
|
|
|
if(!result && mech) {
|
|
sasl->curmech = mech;
|
|
if(Curl_bufref_ptr(&resp))
|
|
result = build_message(sasl, &resp);
|
|
|
|
if(sasl->params->maxirlen &&
|
|
strlen(mech) + Curl_bufref_len(&resp) > sasl->params->maxirlen)
|
|
Curl_bufref_free(&resp);
|
|
|
|
if(!result)
|
|
result = sasl->params->sendauth(data, mech, &resp);
|
|
|
|
if(!result) {
|
|
*progress = SASL_INPROGRESS;
|
|
state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1);
|
|
}
|
|
}
|
|
|
|
Curl_bufref_free(&resp);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Curl_sasl_continue()
|
|
*
|
|
* Continue the authentication.
|
|
*/
|
|
CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
|
|
int code, saslprogress *progress)
|
|
{
|
|
CURLcode result = CURLE_OK;
|
|
struct connectdata *conn = data->conn;
|
|
saslstate newstate = SASL_FINAL;
|
|
struct bufref resp;
|
|
const char *hostname, *disp_hostname;
|
|
int port;
|
|
#if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \
|
|
defined(USE_NTLM)
|
|
const char *service = data->set.str[STRING_SERVICE_NAME] ?
|
|
data->set.str[STRING_SERVICE_NAME] :
|
|
sasl->params->service;
|
|
#endif
|
|
const char *oauth_bearer = data->set.str[STRING_BEARER];
|
|
struct bufref serverdata;
|
|
|
|
Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
|
|
Curl_bufref_init(&serverdata);
|
|
Curl_bufref_init(&resp);
|
|
*progress = SASL_INPROGRESS;
|
|
|
|
if(sasl->state == SASL_FINAL) {
|
|
if(code != sasl->params->finalcode)
|
|
result = CURLE_LOGIN_DENIED;
|
|
*progress = SASL_DONE;
|
|
state(sasl, data, SASL_STOP);
|
|
return result;
|
|
}
|
|
|
|
if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP &&
|
|
code != sasl->params->contcode) {
|
|
*progress = SASL_DONE;
|
|
state(sasl, data, SASL_STOP);
|
|
return CURLE_LOGIN_DENIED;
|
|
}
|
|
|
|
switch(sasl->state) {
|
|
case SASL_STOP:
|
|
*progress = SASL_DONE;
|
|
return result;
|
|
case SASL_PLAIN:
|
|
result = Curl_auth_create_plain_message(conn->sasl_authzid,
|
|
conn->user, conn->passwd, &resp);
|
|
break;
|
|
case SASL_LOGIN:
|
|
result = Curl_auth_create_login_message(conn->user, &resp);
|
|
newstate = SASL_LOGIN_PASSWD;
|
|
break;
|
|
case SASL_LOGIN_PASSWD:
|
|
result = Curl_auth_create_login_message(conn->passwd, &resp);
|
|
break;
|
|
case SASL_EXTERNAL:
|
|
result = Curl_auth_create_external_message(conn->user, &resp);
|
|
break;
|
|
#ifndef CURL_DISABLE_CRYPTO_AUTH
|
|
#ifdef USE_GSASL
|
|
case SASL_GSASL:
|
|
result = get_server_message(sasl, data, &serverdata);
|
|
if(!result)
|
|
result = Curl_auth_gsasl_token(data, &serverdata, &conn->gsasl, &resp);
|
|
if(!result && Curl_bufref_len(&resp) > 0)
|
|
newstate = SASL_GSASL;
|
|
break;
|
|
#endif
|
|
case SASL_CRAMMD5:
|
|
result = get_server_message(sasl, data, &serverdata);
|
|
if(!result)
|
|
result = Curl_auth_create_cram_md5_message(&serverdata, conn->user,
|
|
conn->passwd, &resp);
|
|
break;
|
|
case SASL_DIGESTMD5:
|
|
result = get_server_message(sasl, data, &serverdata);
|
|
if(!result)
|
|
result = Curl_auth_create_digest_md5_message(data, &serverdata,
|
|
conn->user, conn->passwd,
|
|
service, &resp);
|
|
if(!result && (sasl->params->flags & SASL_FLAG_BASE64))
|
|
newstate = SASL_DIGESTMD5_RESP;
|
|
break;
|
|
case SASL_DIGESTMD5_RESP:
|
|
/* Keep response NULL to output an empty line. */
|
|
break;
|
|
#endif
|
|
|
|
#ifdef USE_NTLM
|
|
case SASL_NTLM:
|
|
/* Create the type-1 message */
|
|
result = Curl_auth_create_ntlm_type1_message(data,
|
|
conn->user, conn->passwd,
|
|
service, hostname,
|
|
&conn->ntlm, &resp);
|
|
newstate = SASL_NTLM_TYPE2MSG;
|
|
break;
|
|
case SASL_NTLM_TYPE2MSG:
|
|
/* Decode the type-2 message */
|
|
result = get_server_message(sasl, data, &serverdata);
|
|
if(!result)
|
|
result = Curl_auth_decode_ntlm_type2_message(data, &serverdata,
|
|
&conn->ntlm);
|
|
if(!result)
|
|
result = Curl_auth_create_ntlm_type3_message(data, conn->user,
|
|
conn->passwd, &conn->ntlm,
|
|
&resp);
|
|
break;
|
|
#endif
|
|
|
|
#if defined(USE_KERBEROS5)
|
|
case SASL_GSSAPI:
|
|
result = Curl_auth_create_gssapi_user_message(data, conn->user,
|
|
conn->passwd,
|
|
service,
|
|
conn->host.name,
|
|
sasl->mutual_auth, NULL,
|
|
&conn->krb5,
|
|
&resp);
|
|
newstate = SASL_GSSAPI_TOKEN;
|
|
break;
|
|
case SASL_GSSAPI_TOKEN:
|
|
result = get_server_message(sasl, data, &serverdata);
|
|
if(!result) {
|
|
if(sasl->mutual_auth) {
|
|
/* Decode the user token challenge and create the optional response
|
|
message */
|
|
result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
|
|
NULL, NULL,
|
|
sasl->mutual_auth,
|
|
&serverdata,
|
|
&conn->krb5,
|
|
&resp);
|
|
newstate = SASL_GSSAPI_NO_DATA;
|
|
}
|
|
else
|
|
/* Decode the security challenge and create the response message */
|
|
result = Curl_auth_create_gssapi_security_message(data,
|
|
conn->sasl_authzid,
|
|
&serverdata,
|
|
&conn->krb5,
|
|
&resp);
|
|
}
|
|
break;
|
|
case SASL_GSSAPI_NO_DATA:
|
|
/* Decode the security challenge and create the response message */
|
|
result = get_server_message(sasl, data, &serverdata);
|
|
if(!result)
|
|
result = Curl_auth_create_gssapi_security_message(data,
|
|
conn->sasl_authzid,
|
|
&serverdata,
|
|
&conn->krb5,
|
|
&resp);
|
|
break;
|
|
#endif
|
|
|
|
case SASL_OAUTH2:
|
|
/* Create the authorization message */
|
|
if(sasl->authused == SASL_MECH_OAUTHBEARER) {
|
|
result = Curl_auth_create_oauth_bearer_message(conn->user,
|
|
hostname,
|
|
port,
|
|
oauth_bearer,
|
|
&resp);
|
|
|
|
/* Failures maybe sent by the server as continuations for OAUTHBEARER */
|
|
newstate = SASL_OAUTH2_RESP;
|
|
}
|
|
else
|
|
result = Curl_auth_create_xoauth_bearer_message(conn->user,
|
|
oauth_bearer,
|
|
&resp);
|
|
break;
|
|
|
|
case SASL_OAUTH2_RESP:
|
|
/* The continuation is optional so check the response code */
|
|
if(code == sasl->params->finalcode) {
|
|
/* Final response was received so we are done */
|
|
*progress = SASL_DONE;
|
|
state(sasl, data, SASL_STOP);
|
|
return result;
|
|
}
|
|
else if(code == sasl->params->contcode) {
|
|
/* Acknowledge the continuation by sending a 0x01 response. */
|
|
Curl_bufref_set(&resp, "\x01", 1, NULL);
|
|
break;
|
|
}
|
|
else {
|
|
*progress = SASL_DONE;
|
|
state(sasl, data, SASL_STOP);
|
|
return CURLE_LOGIN_DENIED;
|
|
}
|
|
|
|
case SASL_CANCEL:
|
|
/* Remove the offending mechanism from the supported list */
|
|
sasl->authmechs ^= sasl->authused;
|
|
|
|
/* Start an alternative SASL authentication */
|
|
return Curl_sasl_start(sasl, data, sasl->force_ir, progress);
|
|
default:
|
|
failf(data, "Unsupported SASL authentication mechanism");
|
|
result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */
|
|
break;
|
|
}
|
|
|
|
Curl_bufref_free(&serverdata);
|
|
|
|
switch(result) {
|
|
case CURLE_BAD_CONTENT_ENCODING:
|
|
/* Cancel dialog */
|
|
result = sasl->params->cancelauth(data, sasl->curmech);
|
|
newstate = SASL_CANCEL;
|
|
break;
|
|
case CURLE_OK:
|
|
result = build_message(sasl, &resp);
|
|
if(!result)
|
|
result = sasl->params->contauth(data, sasl->curmech, &resp);
|
|
break;
|
|
default:
|
|
newstate = SASL_STOP; /* Stop on error */
|
|
*progress = SASL_DONE;
|
|
break;
|
|
}
|
|
|
|
Curl_bufref_free(&resp);
|
|
|
|
state(sasl, data, newstate);
|
|
|
|
return result;
|
|
}
|
|
#endif /* protocols are enabled that use SASL */
|