diff options
Diffstat (limited to 'Utilities/cmcurl/lib/curl_sasl.c')
-rw-r--r-- | Utilities/cmcurl/lib/curl_sasl.c | 302 |
1 files changed, 191 insertions, 111 deletions
diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c index ffeb751..a4d1059 100644 --- a/Utilities/cmcurl/lib/curl_sasl.c +++ b/Utilities/cmcurl/lib/curl_sasl.c @@ -23,6 +23,8 @@ * 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> @@ -56,7 +58,7 @@ static const struct { const char *name; /* Name */ size_t len; /* Name length */ - unsigned int bit; /* Flag bit */ + unsigned short bit; /* Flag bit */ } mechtable[] = { { "LOGIN", 5, SASL_MECH_LOGIN }, { "PLAIN", 5, SASL_MECH_PLAIN }, @@ -67,6 +69,8 @@ static const struct { { "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 } }; @@ -90,6 +94,13 @@ void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused) } #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) { @@ -117,7 +128,8 @@ void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused) * * Returns the SASL mechanism token or 0 if no match. */ -unsigned int Curl_sasl_decode_mech(const char *ptr, size_t maxlen, size_t *len) +unsigned short Curl_sasl_decode_mech(const char *ptr, size_t maxlen, + size_t *len) { unsigned int i; char c; @@ -162,7 +174,7 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, if(!strncmp(value, "*", len)) sasl->prefmech = SASL_AUTH_DEFAULT; else { - unsigned int mechbit = Curl_sasl_decode_mech(value, len, &mechlen); + unsigned short mechbit = Curl_sasl_decode_mech(value, len, &mechlen); if(mechbit && mechlen == len) sasl->prefmech |= mechbit; else @@ -215,6 +227,7 @@ static void state(struct SASL *sasl, struct Curl_easy *data, "GSSAPI_NO_DATA", "OAUTH2", "OAUTH2_RESP", + "GSASL", "CANCEL", "FINAL", /* LAST */ @@ -230,6 +243,49 @@ static void state(struct SASL *sasl, struct Curl_easy *data, 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) +{ + unsigned char *msg; + size_t msglen; + char *serverdata = NULL; + CURLcode result = CURLE_OK; + + sasl->params->getmessage(data->state.buffer, &serverdata); + if(!serverdata) + result = CURLE_BAD_CONTENT_ENCODING; + else 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 Curl_easy *data, struct bufref *msg) +{ + CURLcode result = CURLE_OK; + char *base64; + size_t base64len; + + if(!Curl_bufref_ptr(msg)) /* Empty mesage. */ + Curl_bufref_set(msg, "", 0, NULL); + else if(!Curl_bufref_len(msg)) /* Explicit empty response. */ + Curl_bufref_set(msg, "=", 1, NULL); + else { + result = Curl_base64_encode(data, (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() * @@ -260,25 +316,21 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, CURLcode result = CURLE_OK; unsigned int enabledmechs; const char *mech = NULL; - char *resp = NULL; - size_t len = 0; + struct bufref resp; saslstate state1 = SASL_STOP; saslstate state2 = SASL_FINAL; -#ifndef CURL_DISABLE_PROXY - const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : - conn->host.name; - const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; -#else - const char * const hostname = conn->host.name; - const long int port = conn->remote_port; -#endif + const char * const hostname = SSL_HOST_NAME(); + const long int port = SSL_HOST_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_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; @@ -292,8 +344,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, sasl->authused = SASL_MECH_EXTERNAL; if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_external_message(data, conn->user, &resp, - &len); + result = Curl_auth_create_external_message(conn->user, &resp); } else if(conn->bits.user_passwd) { #if defined(USE_KERBEROS5) @@ -309,10 +360,39 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, result = Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd, service, - data->conn->host.name, + conn->host.name, sasl->mutual_auth, NULL, &conn->krb5, - &resp, &len); + &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 @@ -342,8 +422,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, conn->user, conn->passwd, service, hostname, - &conn->ntlm, &resp, - &len); + &conn->ntlm, &resp); } else #endif @@ -354,11 +433,11 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, sasl->authused = SASL_MECH_OAUTHBEARER; if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_oauth_bearer_message(data, conn->user, + result = Curl_auth_create_oauth_bearer_message(conn->user, hostname, port, oauth_bearer, - &resp, &len); + &resp); } else if((enabledmechs & SASL_MECH_XOAUTH2) && oauth_bearer) { mech = SASL_MECH_STRING_XOAUTH2; @@ -366,9 +445,9 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, sasl->authused = SASL_MECH_XOAUTH2; if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_xoauth_bearer_message(data, conn->user, + result = Curl_auth_create_xoauth_bearer_message(conn->user, oauth_bearer, - &resp, &len); + &resp); } else if(enabledmechs & SASL_MECH_PLAIN) { mech = SASL_MECH_STRING_PLAIN; @@ -376,9 +455,9 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, sasl->authused = SASL_MECH_PLAIN; if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_plain_message(data, conn->sasl_authzid, + result = Curl_auth_create_plain_message(conn->sasl_authzid, conn->user, conn->passwd, - &resp, &len); + &resp); } else if(enabledmechs & SASL_MECH_LOGIN) { mech = SASL_MECH_STRING_LOGIN; @@ -387,26 +466,29 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, sasl->authused = SASL_MECH_LOGIN; if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_login_message(data, conn->user, &resp, &len); + result = Curl_auth_create_login_message(conn->user, &resp); } } if(!result && mech) { - if(resp && sasl->params->maxirlen && - strlen(mech) + len > sasl->params->maxirlen) { - free(resp); - resp = NULL; - } + if(Curl_bufref_ptr(&resp)) + result = build_message(data, &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, conn, mech, + (const char *) Curl_bufref_ptr(&resp)); - result = sasl->params->sendauth(data, conn, mech, resp); if(!result) { *progress = SASL_INPROGRESS; - state(sasl, data, resp ? state2 : state1); + state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1); } } - free(resp); - + Curl_bufref_free(&resp); return result; } @@ -421,29 +503,20 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, { CURLcode result = CURLE_OK; saslstate newstate = SASL_FINAL; - char *resp = NULL; -#ifndef CURL_DISABLE_PROXY - const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : - conn->host.name; - const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; -#else - const char * const hostname = conn->host.name; - const long int port = conn->remote_port; -#endif -#if !defined(CURL_DISABLE_CRYPTO_AUTH) - char *chlg = NULL; - size_t chlglen = 0; -#endif + struct bufref resp; + const char * const hostname = SSL_HOST_NAME(); + const long int port = SSL_HOST_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; - char *serverdata; #endif - size_t len = 0; const char *oauth_bearer = data->set.str[STRING_BEARER]; + struct bufref serverdata; + Curl_bufref_init(&serverdata); + Curl_bufref_init(&resp); *progress = SASL_INPROGRESS; if(sasl->state == SASL_FINAL) { @@ -466,42 +539,45 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, *progress = SASL_DONE; return result; case SASL_PLAIN: - result = Curl_auth_create_plain_message(data, conn->sasl_authzid, - conn->user, conn->passwd, - &resp, &len); + result = Curl_auth_create_plain_message(conn->sasl_authzid, + conn->user, conn->passwd, &resp); break; case SASL_LOGIN: - result = Curl_auth_create_login_message(data, conn->user, &resp, &len); + result = Curl_auth_create_login_message(conn->user, &resp); newstate = SASL_LOGIN_PASSWD; break; case SASL_LOGIN_PASSWD: - result = Curl_auth_create_login_message(data, conn->passwd, &resp, &len); + result = Curl_auth_create_login_message(conn->passwd, &resp); break; case SASL_EXTERNAL: - result = Curl_auth_create_external_message(data, conn->user, &resp, &len); + 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: - sasl->params->getmessage(data->state.buffer, &serverdata); - result = Curl_auth_decode_cram_md5_message(serverdata, &chlg, &chlglen); + result = get_server_message(sasl, data, &serverdata); if(!result) - result = Curl_auth_create_cram_md5_message(data, chlg, conn->user, - conn->passwd, &resp, &len); - free(chlg); + result = Curl_auth_create_cram_md5_message(&serverdata, conn->user, + conn->passwd, &resp); break; case SASL_DIGESTMD5: - sasl->params->getmessage(data->state.buffer, &serverdata); - result = Curl_auth_create_digest_md5_message(data, serverdata, - conn->user, conn->passwd, - service, - &resp, &len); + result = get_server_message(sasl, data, &serverdata); + if(!result) + result = Curl_auth_create_digest_md5_message(data, &serverdata, + conn->user, conn->passwd, + service, &resp); newstate = SASL_DIGESTMD5_RESP; break; case SASL_DIGESTMD5_RESP: - resp = strdup(""); - if(!resp) - result = CURLE_OUT_OF_MEMORY; + /* Keep response NULL to output an empty line. */ break; #endif @@ -511,18 +587,19 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, result = Curl_auth_create_ntlm_type1_message(data, conn->user, conn->passwd, service, hostname, - &conn->ntlm, &resp, &len); + &conn->ntlm, &resp); newstate = SASL_NTLM_TYPE2MSG; break; case SASL_NTLM_TYPE2MSG: /* Decode the type-2 message */ - sasl->params->getmessage(data->state.buffer, &serverdata); - result = Curl_auth_decode_ntlm_type2_message(data, serverdata, - &conn->ntlm); + 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, &len); + &resp); break; #endif @@ -531,55 +608,59 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, result = Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd, service, - data->conn->host.name, + conn->host.name, sasl->mutual_auth, NULL, &conn->krb5, - &resp, &len); + &resp); 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_auth_create_gssapi_user_message(data, NULL, NULL, - NULL, NULL, - sasl->mutual_auth, - serverdata, &conn->krb5, - &resp, &len); - newstate = SASL_GSSAPI_NO_DATA; + 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, &serverdata, + &conn->krb5, + &resp); } - else - /* Decode the security challenge and create the response message */ - result = Curl_auth_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_auth_create_gssapi_security_message(data, serverdata, - &conn->krb5, - &resp, &len); + result = get_server_message(sasl, data, &serverdata); + if(!result) + result = Curl_auth_create_gssapi_security_message(data, &serverdata, + &conn->krb5, + &resp); break; #endif case SASL_OAUTH2: /* Create the authorisation message */ if(sasl->authused == SASL_MECH_OAUTHBEARER) { - result = Curl_auth_create_oauth_bearer_message(data, conn->user, + result = Curl_auth_create_oauth_bearer_message(conn->user, hostname, port, oauth_bearer, - &resp, &len); + &resp); /* Failures maybe sent by the server as continuations for OAUTHBEARER */ newstate = SASL_OAUTH2_RESP; } else - result = Curl_auth_create_xoauth_bearer_message(data, conn->user, + result = Curl_auth_create_xoauth_bearer_message(conn->user, oauth_bearer, - &resp, &len); + &resp); break; case SASL_OAUTH2_RESP: @@ -591,11 +672,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, return result; } else if(code == sasl->params->contcode) { - /* Acknowledge the continuation by sending a 0x01 response base64 - encoded */ - resp = strdup("AQ=="); - if(!resp) - result = CURLE_OUT_OF_MEMORY; + /* Acknowledge the continuation by sending a 0x01 response. */ + Curl_bufref_set(&resp, "\x01", 1, NULL); break; } else { @@ -609,15 +687,15 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, sasl->authmechs ^= sasl->authused; /* Start an alternative SASL authentication */ - result = Curl_sasl_start(sasl, data, conn, sasl->force_ir, progress); - newstate = sasl->state; /* Use state from Curl_sasl_start() */ - break; + return Curl_sasl_start(sasl, data, conn, 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 */ @@ -625,8 +703,10 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, newstate = SASL_CANCEL; break; case CURLE_OK: - if(resp) - result = sasl->params->sendcont(data, conn, resp); + result = build_message(data, &resp); + if(!result) + result = sasl->params->sendcont(data, conn, + (const char *) Curl_bufref_ptr(&resp)); break; default: newstate = SASL_STOP; /* Stop on error */ @@ -634,7 +714,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, break; } - free(resp); + Curl_bufref_free(&resp); state(sasl, data, newstate); |