summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Josefsson <simon@josefsson.org>2020-12-24 19:48:45 +0100
committerDaniel Stenberg <daniel@haxx.se>2021-02-09 10:53:25 +0100
commit3eebbfe8f34d37c4d68d08277a44ec7aa6bd0889 (patch)
tree0e8140b9cad228433a94303b37b9358e1502efb7
parentcb2dc1ba89dbdb3850de8e92907bbbbb9c1903e0 (diff)
downloadcurl-3eebbfe8f34d37c4d68d08277a44ec7aa6bd0889.tar.gz
sasl: support SCRAM-SHA-1 and SCRAM-SHA-256 via libgsasl
Closes #6372
-rwxr-xr-xconfigure.ac28
-rw-r--r--lib/Makefile.inc1
-rw-r--r--lib/curl_sasl.c52
-rw-r--r--lib/curl_sasl.h5
-rw-r--r--lib/urldata.h13
-rw-r--r--lib/vauth/gsasl.c137
-rw-r--r--lib/vauth/vauth.h25
7 files changed, 261 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index e72aca604..b7aef5ba3 100755
--- a/configure.ac
+++ b/configure.ac
@@ -149,6 +149,7 @@ dnl initialize all the info variables
curl_brotli_msg="no (--with-brotli)"
curl_zstd_msg="no (--with-zstd)"
curl_gss_msg="no (--with-gssapi)"
+ curl_gsasl_msg="no (--with-gsasl)"
curl_tls_srp_msg="no (--enable-tls-srp)"
curl_res_msg="default (--enable-ares / --enable-threaded-resolver)"
curl_ipv6_msg="no (--enable-ipv6)"
@@ -2895,6 +2896,28 @@ if test $with_libpsl != "no"; then
fi
AM_CONDITIONAL([USE_LIBPSL], [test "$curl_psl_msg" = "enabled"])
+
+dnl **********************************************************************
+dnl Check for libgsasl
+dnl **********************************************************************
+
+AC_ARG_WITH(libgsasl,
+ AS_HELP_STRING([--without-libgsasl],
+ [disable libgsasl support for SCRAM]),
+ with_libgsasl=$withval,
+ with_libgsasl=yes)
+if test $with_libgsasl != "no"; then
+ AC_SEARCH_LIBS(gsasl_init, gsasl,
+ [curl_gsasl_msg="enabled";
+ AC_DEFINE([USE_GSASL], [1], [GSASL support enabled])
+ ],
+ [curl_gsasl_msg="no (libgsasl not found)";
+ AC_MSG_WARN([libgsasl was not found])
+ ]
+ )
+fi
+AM_CONDITIONAL([USE_GSASL], [test "$curl_gsasl_msg" = "enabled"])
+
dnl **********************************************************************
dnl Check for libmetalink
dnl **********************************************************************
@@ -5103,6 +5126,10 @@ if test "x$curl_psl_msg" = "xenabled"; then
SUPPORT_FEATURES="$SUPPORT_FEATURES PSL"
fi
+if test "x$curl_gsasl_msg" = "xenabled"; then
+ SUPPORT_FEATURES="$SUPPORT_FEATURES GSASL"
+fi
+
if test "x$enable_altsvc" = "xyes"; then
SUPPORT_FEATURES="$SUPPORT_FEATURES alt-svc"
fi
@@ -5337,6 +5364,7 @@ AC_MSG_NOTICE([Configured to build curl/libcurl:
brotli: ${curl_brotli_msg}
zstd: ${curl_zstd_msg}
GSS-API: ${curl_gss_msg}
+ GSASL: ${curl_gsasl_msg}
TLS-SRP: ${curl_tls_srp_msg}
resolver: ${curl_res_msg}
IPv6: ${curl_ipv6_msg}
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index e8d2259f2..dddb4a491 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -25,6 +25,7 @@ LIB_VAUTH_CFILES = \
vauth/cram.c \
vauth/digest.c \
vauth/digest_sspi.c \
+ vauth/gsasl.c \
vauth/krb5_gssapi.c \
vauth/krb5_sspi.c \
vauth/ntlm.c \
diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c
index ffeb75164..998f4c68f 100644
--- a/lib/curl_sasl.c
+++ b/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>
@@ -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) {
@@ -215,6 +226,7 @@ static void state(struct SASL *sasl, struct Curl_easy *data,
"GSSAPI_NO_DATA",
"OAUTH2",
"OAUTH2_RESP",
+ "GSASL",
"CANCEL",
"FINAL",
/* LAST */
@@ -316,6 +328,37 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
}
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, NULL, &conn->gsasl,
+ &resp, &len);
+ }
+ 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, NULL, &conn->gsasl,
+ &resp, &len);
+ }
+ else
+#endif
#ifndef CURL_DISABLE_CRYPTO_AUTH
if((enabledmechs & SASL_MECH_DIGEST_MD5) &&
Curl_auth_is_digest_supported()) {
@@ -481,6 +524,15 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
result = Curl_auth_create_external_message(data, conn->user, &resp, &len);
break;
+#ifdef USE_GSASL
+ case SASL_GSASL:
+ sasl->params->getmessage(data->state.buffer, &serverdata);
+ result = Curl_auth_gsasl_token(data, serverdata, &conn->gsasl,
+ &resp, &len);
+ if(len > 0)
+ newstate = SASL_GSASL;
+ break;
+#endif
#ifndef CURL_DISABLE_CRYPTO_AUTH
case SASL_CRAMMD5:
sasl->params->getmessage(data->state.buffer, &serverdata);
diff --git a/lib/curl_sasl.h b/lib/curl_sasl.h
index 75a957583..8648c632b 100644
--- a/lib/curl_sasl.h
+++ b/lib/curl_sasl.h
@@ -37,6 +37,8 @@ struct connectdata;
#define SASL_MECH_NTLM (1 << 6)
#define SASL_MECH_XOAUTH2 (1 << 7)
#define SASL_MECH_OAUTHBEARER (1 << 8)
+#define SASL_MECH_SCRAM_SHA_1 (1 << 9)
+#define SASL_MECH_SCRAM_SHA_256 (1 << 10)
/* Authentication mechanism values */
#define SASL_AUTH_NONE 0
@@ -53,6 +55,8 @@ struct connectdata;
#define SASL_MECH_STRING_NTLM "NTLM"
#define SASL_MECH_STRING_XOAUTH2 "XOAUTH2"
#define SASL_MECH_STRING_OAUTHBEARER "OAUTHBEARER"
+#define SASL_MECH_STRING_SCRAM_SHA_1 "SCRAM-SHA-1"
+#define SASL_MECH_STRING_SCRAM_SHA_256 "SCRAM-SHA-256"
/* SASL machine states */
typedef enum {
@@ -71,6 +75,7 @@ typedef enum {
SASL_GSSAPI_NO_DATA,
SASL_OAUTH2,
SASL_OAUTH2_RESP,
+ SASL_GSASL,
SASL_CANCEL,
SASL_FINAL
} saslstate;
diff --git a/lib/urldata.h b/lib/urldata.h
index 97ad49372..4d7ccd59e 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -371,6 +371,15 @@ struct kerberos5data {
};
#endif
+/* Struct used for SCRAM-SHA-1 authentication */
+#ifdef USE_GSASL
+#include <gsasl.h>
+struct gsasldata {
+ Gsasl *ctx;
+ Gsasl_session *client;
+};
+#endif
+
/* Struct used for NTLM challenge-response authentication */
#if defined(USE_NTLM)
struct ntlmdata {
@@ -1061,6 +1070,10 @@ struct connectdata {
CtxtHandle *sslContext;
#endif
+#ifdef USE_GSASL
+ struct gsasldata gsasl;
+#endif
+
#if defined(USE_NTLM)
curlntlm http_ntlm_state;
curlntlm proxy_ntlm_state;
diff --git a/lib/vauth/gsasl.c b/lib/vauth/gsasl.c
new file mode 100644
index 000000000..c9b1576f8
--- /dev/null
+++ b/lib/vauth/gsasl.c
@@ -0,0 +1,137 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2020 - 2021, Simon Josefsson, <simon@josefsson.org>, 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.
+ *
+ * RFC5802 SCRAM-SHA-1 authentication
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_GSASL
+
+#include <curl/curl.h>
+
+#include "curl_base64.h"
+#include "vauth/vauth.h"
+#include "urldata.h"
+#include "sendf.h"
+
+#include <gsasl.h>
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+bool Curl_auth_gsasl_is_supported(struct Curl_easy *data,
+ const char *mech,
+ struct gsasldata *gsasl)
+{
+ int res;
+
+ res = gsasl_init(&gsasl->ctx);
+ if(res != GSASL_OK) {
+ failf(data, "gsasl init: %s\n", gsasl_strerror(res));
+ return FALSE;
+ }
+
+ res = gsasl_client_start(gsasl->ctx, mech, &gsasl->client);
+ if(res != GSASL_OK) {
+ gsasl_done(gsasl->ctx);
+ return FALSE;
+ }
+
+ return true;
+}
+
+CURLcode Curl_auth_gsasl_start(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ struct gsasldata *gsasl)
+{
+#if GSASL_VERSION_NUMBER >= 0x010a00
+ int res;
+ res =
+#endif
+ gsasl_property_set(gsasl->client, GSASL_AUTHID, userp);
+#if GSASL_VERSION_NUMBER >= 0x010a00
+ if(res != GSASL_OK) {
+ failf(data, "setting AUTHID failed: %s\n", gsasl_strerror(result));
+ return CURLE_OUT_OF_MEMORY;
+ }
+#endif
+
+#if GSASL_VERSION_NUMBER >= 0x010a00
+ res =
+#endif
+ gsasl_property_set(gsasl->client, GSASL_PASSWORD, passwdp);
+#if GSASL_VERSION_NUMBER >= 0x010a00
+ if(res != GSASL_OK) {
+ failf(data, "setting PASSWORD failed: %s\n", gsasl_strerror(result));
+ return CURLE_OUT_OF_MEMORY;
+ }
+#endif
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_auth_gsasl_token(struct Curl_easy *data,
+ const char *chlg64,
+ struct gsasldata *gsasl,
+ char **outptr, size_t *outlen)
+{
+ unsigned char *chlg = NULL;
+ size_t chlglen = 0;
+ size_t chlg64len = chlg64 ? strlen(chlg64) : 0;
+ int result;
+ char *response;
+
+ if(chlg64) {
+ result = Curl_base64_decode(chlg64, &chlg, &chlglen);
+ if(result)
+ return result;
+ }
+
+ result = gsasl_step(gsasl->client, chlg, chlglen, &response, outlen);
+ if(result != GSASL_OK && result != GSASL_NEEDS_MORE) {
+ if(chlg64)
+ free(chlg);
+ failf(data, "GSASL step: %s\n", gsasl_strerror(result));
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ if(*outlen > 0) {
+ result = Curl_base64_encode(data, response, 0, outptr, outlen);
+ gsasl_free(response);
+ }
+ else
+ *outptr = strdup("");
+
+ return CURLE_OK;
+}
+
+void Curl_auth_gsasl_cleanup(struct gsasldata *gsasl)
+{
+ gsasl_finish(gsasl->client);
+ gsasl->client = NULL;
+
+ gsasl_done(gsasl->ctx);
+ gsasl->ctx = NULL;
+}
+#endif
diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h
index f25cfc329..e7728c4ba 100644
--- a/lib/vauth/vauth.h
+++ b/lib/vauth/vauth.h
@@ -42,6 +42,10 @@ struct kerberos5data;
struct negotiatedata;
#endif
+#if defined(USE_GSASL)
+struct gsasldata;
+#endif
+
#if defined(USE_WINDOWS_SSPI)
#define GSS_ERROR(status) ((status) & 0x80000000)
#endif
@@ -115,6 +119,27 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
void Curl_auth_digest_cleanup(struct digestdata *digest);
#endif /* !CURL_DISABLE_CRYPTO_AUTH */
+#ifdef USE_GSASL
+/* This is used to evaluate if MECH is supported by gsasl */
+bool Curl_auth_gsasl_is_supported(struct Curl_easy *data,
+ const char *mech,
+ struct gsasldata *gsasl);
+/* This is used to start a gsasl method */
+CURLcode Curl_auth_gsasl_start(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ struct gsasldata *gsasl);
+
+/* This is used to process and generate a new SASL token */
+CURLcode Curl_auth_gsasl_token(struct Curl_easy *data,
+ const char *chlg64,
+ struct gsasldata *gsasl,
+ char **outptr, size_t *outlen);
+
+/* This is used to clean up the gsasl specific data */
+void Curl_auth_gsasl_cleanup(struct gsasldata *digest);
+#endif
+
#if defined(USE_NTLM)
/* This is used to evaluate if NTLM is supported */
bool Curl_auth_is_ntlm_supported(void);