aboutsummaryrefslogtreecommitdiff
path: root/lib/curl_sasl.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/curl_sasl.c')
-rw-r--r--lib/curl_sasl.c331
1 files changed, 330 insertions, 1 deletions
diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c
index 7cb575b33..6a648f0ae 100644
--- a/lib/curl_sasl.c
+++ b/lib/curl_sasl.c
@@ -43,6 +43,7 @@
#include "strtok.h"
#include "strequal.h"
#include "rawstr.h"
+#include "sendf.h"
#include "non-ascii.h" /* included for Curl_convert_... prototypes */
#define _MPRINTF_REPLACE /* use our functions only */
@@ -1271,11 +1272,339 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
*
* Initializes an SASL structure.
*/
-void Curl_sasl_init(struct SASL *sasl)
+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->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) */
+ sasl->force_ir = FALSE; /* Respect external option */
+}
+
+/*
+ * state()
+ *
+ * This is the ONLY way to change SASL state!
+ */
+static void state(struct SASL *sasl,
+ struct connectdata *conn, saslstate newstate)
+{
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* for debug purposes */
+ static const char * const names[]={
+ "STOP",
+ "PLAIN",
+ "LOGIN",
+ "LOGIN_PASSWD",
+ "CRAMMD5",
+ "DIGESTMD5",
+ "DIGESTMD5_RESP",
+ "NTLM",
+ "NTLM_TYPE2MSG",
+ "GSSAPI",
+ "GSSAPI_TOKEN",
+ "GSSAPI_NO_DATA",
+ "XOAUTH2",
+ "CANCEL",
+ "FINAL",
+ /* LAST */
+ };
+
+ if(sasl->state != newstate)
+ infof(conn->data, "SASL %p state change from %s to %s\n",
+ (void *)sasl, names[sasl->state], names[newstate]);
+#endif
+
+ sasl->state = newstate;
+}
+
+/*
+ * Curl_sasl_start()
+ *
+ * Calculate the required login details for SASL authentication.
+ */
+CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
+ bool force_ir, saslprogress *progress)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ unsigned int enabledmechs;
+ const char *mech = NULL;
+ char *resp = NULL;
+ size_t len = 0;
+ saslstate state1 = SASL_STOP;
+ saslstate state2 = SASL_FINAL;
+
+ 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 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
+#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
+#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 = Curl_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 = Curl_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 = Curl_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 &&
+ strlen(mech) + len > sasl->params->maxirlen) {
+ Curl_safefree(resp);
+ resp = NULL;
+ }
+ if(mech) {
+ result = sasl->params->sendauth(conn, mech, resp);
+ if(!result) {
+ *progress = SASL_INPROGRESS;
+ state(sasl, conn, resp? state2: state1);
+ }
+ }
+ }
+
+ Curl_safefree(resp);
+ return result;
+}
+
+/*
+ * Curl_sasl_continue()
+ *
+ * Continue an SASL authentication.
+ */
+CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
+ int code, saslprogress *progress)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ saslstate newstate = SASL_FINAL;
+ char *chlg = NULL;
+ char *resp = NULL;
+ char *serverdata;
+ size_t len = 0;
+ size_t chlglen = 0;
+
+ *progress = SASL_INPROGRESS;
+
+ if(sasl->state == SASL_FINAL) {
+ if(code != sasl->params->finalcode)
+ result = CURLE_LOGIN_DENIED;
+ *progress = SASL_DONE;
+ state(sasl, conn, SASL_STOP);
+ return result;
+ }
+
+ if(sasl->state != SASL_CANCEL && code != sasl->params->contcode) {
+ *progress = SASL_DONE;
+ state(sasl, conn, SASL_STOP);
+ return CURLE_LOGIN_DENIED;
+ }
+
+ switch(sasl->state) {
+ case SASL_STOP:
+ *progress = SASL_DONE;
+ return result;
+ case SASL_PLAIN:
+ result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
+ &resp, &len);
+ break;
+ case SASL_LOGIN:
+ result = Curl_sasl_create_login_message(data, conn->user, &resp, &len);
+ newstate = SASL_LOGIN_PASSWD;
+ break;
+ case SASL_LOGIN_PASSWD:
+ result = Curl_sasl_create_login_message(data, conn->passwd, &resp, &len);
+ break;
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ case SASL_CRAMMD5:
+ sasl->params->getmessage(data->state.buffer, &serverdata);
+ result = Curl_sasl_decode_cram_md5_message(serverdata, &chlg, &chlglen);
+ if(!result)
+ result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
+ conn->passwd, &resp, &len);
+ Curl_safefree(chlg);
+ break;
+ case SASL_DIGESTMD5:
+ sasl->params->getmessage(data->state.buffer, &serverdata);
+ result = Curl_sasl_create_digest_md5_message(data, serverdata,
+ conn->user, conn->passwd,
+ sasl->params->service,
+ &resp, &len);
+ newstate = SASL_DIGESTMD5_RESP;
+ break;
+ case SASL_DIGESTMD5_RESP:
+ if(!(resp = strdup("")))
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+#endif
+
+#ifdef USE_NTLM
+ case SASL_NTLM:
+ /* Create the type-1 message */
+ result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
+ &conn->ntlm,
+ &resp, &len);
+ newstate = SASL_NTLM_TYPE2MSG;
+ break;
+ case SASL_NTLM_TYPE2MSG:
+ /* Decode the type-2 message */
+ sasl->params->getmessage(data->state.buffer, &serverdata);
+ result = Curl_sasl_decode_ntlm_type2_message(data,
+ serverdata, &conn->ntlm);
+ if(!result)
+ result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
+ conn->passwd, &conn->ntlm,
+ &resp, &len);
+ break;
+#endif
+
+#if defined(USE_KERBEROS5)
+ case SASL_GSSAPI:
+ result = Curl_sasl_create_gssapi_user_message(data, conn->user,
+ conn->passwd,
+ sasl->params->service,
+ sasl->mutual_auth, NULL,
+ &conn->krb5,
+ &resp, &len);
+ newstate = SASL_GSSAPI_TOKEN;
+ break;
+ case SASL_GSSAPI_TOKEN:
+ sasl->params->getmessage(data->state.buffer, &serverdata);
+ if(sasl->mutual_auth) {
+ /* Decode the user token challenge and create the optional response
+ message */
+ result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL,
+ sasl->mutual_auth,
+ serverdata, &conn->krb5,
+ &resp, &len);
+ newstate = SASL_GSSAPI_NO_DATA;
+ }
+ else
+ /* Decode the security challenge and create the response message */
+ result = Curl_sasl_create_gssapi_security_message(data, serverdata,
+ &conn->krb5,
+ &resp, &len);
+ break;
+ case SASL_GSSAPI_NO_DATA:
+ sasl->params->getmessage(data->state.buffer, &serverdata);
+ /* Decode the security challenge and create the response message */
+ result = Curl_sasl_create_gssapi_security_message(data, serverdata,
+ &conn->krb5,
+ &resp, &len);
+ break;
+#endif
+
+ case SASL_XOAUTH2:
+ /* Create the authorisation message */
+ result = Curl_sasl_create_xoauth2_message(data, conn->user,
+ conn->xoauth2_bearer,
+ &resp, &len);
+ break;
+ case SASL_CANCEL:
+ /* Remove the offending mechanism from the supported list */
+ sasl->authmechs ^= sasl->authused;
+
+ /* Start an alternative SASL authentication */
+ result = Curl_sasl_start(sasl, conn, sasl->force_ir, progress);
+ newstate = sasl->state; /* Use state from Curl_sasl_start() */
+ break;
+ default:
+ failf(data, "Unsupported SASL authentication mechanism");
+ result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */
+ break;
+ }
+
+ switch(result) {
+ case CURLE_BAD_CONTENT_ENCODING:
+ /* Cancel dialog */
+ result = sasl->params->sendcont(conn, "*");
+ newstate = SASL_CANCEL;
+ break;
+ case CURLE_OK:
+ if(resp)
+ result = sasl->params->sendcont(conn, resp);
+ break;
+ default:
+ newstate = SASL_STOP; /* Stop on error */
+ *progress = SASL_DONE;
+ break;
+ }
+
+ Curl_safefree(resp);
+ state(sasl, conn, newstate);
+ return result;
}