Skip to content

Commit

Permalink
sasl: binary messages
Browse files Browse the repository at this point in the history
Capabilities of sasl module are extended to exchange messages in binary
as an alternative to base64.

If http authentication flags have been set, those are used as sasl
default preferred mechanisms.
  • Loading branch information
monnerat committed Jul 8, 2021
1 parent e7416cf commit 3664994
Show file tree
Hide file tree
Showing 6 changed files with 295 additions and 224 deletions.
5 changes: 0 additions & 5 deletions docs/TODO
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@
16. SASL
16.1 Other authentication mechanisms
16.2 Add QOP support to GSSAPI authentication
16.3 Support binary messages (i.e.: non-base64)

17. SSH protocols
17.1 Multiplexing
Expand Down Expand Up @@ -877,10 +876,6 @@
with integrity protection) and auth-conf (Authentication with integrity and
privacy protection).

16.3 Support binary messages (i.e.: non-base64)

Mandatory to support LDAP SASL authentication.


17. SSH protocols

Expand Down
117 changes: 69 additions & 48 deletions lib/curl_sasl.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@

/* Supported mechanisms */
static const struct {
const char *name; /* Name */
size_t len; /* Name length */
const char *name; /* Name */
size_t len; /* Name length */
unsigned short bit; /* Flag bit */
} mechtable[] = {
{ "LOGIN", 5, SASL_MECH_LOGIN },
Expand Down Expand Up @@ -85,8 +85,11 @@ static const struct {
* conn [in] - The connection data.
* authused [in] - The authentication mechanism used.
*/
void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
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) {
Expand All @@ -107,12 +110,6 @@ void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
Curl_auth_cleanup_ntlm(&conn->ntlm);
}
#endif

#if !defined(USE_KERBEROS5) && !defined(USE_NTLM)
/* Reserved for future use */
(void)conn;
(void)authused;
#endif
}

/*
Expand Down Expand Up @@ -189,16 +186,35 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
*
* Initializes the SASL structure.
*/
void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params)
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 = SASL_AUTH_DEFAULT; /* Prefer all mechanisms */
sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */
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;
}
}

/*
Expand Down Expand Up @@ -247,40 +263,45 @@ static void state(struct SASL *sasl, struct Curl_easy *data,
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);
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 Curl_easy *data, struct bufref *msg)
static CURLcode build_message(struct SASL *sasl, 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);
if(sasl->params->flags & SASL_FLAG_BASE64) {
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 {
char *base64;
size_t base64len;

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;
Expand Down Expand Up @@ -310,11 +331,11 @@ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn)
* Calculate the required login details for SASL authentication.
*/
CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
struct connectdata *conn,
bool force_ir, saslprogress *progress)
{
CURLcode result = CURLE_OK;
unsigned int enabledmechs;
struct connectdata *conn = data->conn;
unsigned short enabledmechs;
const char *mech = NULL;
struct bufref resp;
saslstate state1 = SASL_STOP;
Expand Down Expand Up @@ -471,16 +492,16 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
}

if(!result && mech) {
sasl->curmech = mech;
if(Curl_bufref_ptr(&resp))
result = build_message(data, &resp);
result = build_message(sasl, 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, mech, &resp);

if(!result) {
*progress = SASL_INPROGRESS;
Expand All @@ -498,10 +519,10 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
* Continue the authentication.
*/
CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
struct connectdata *conn,
int code, saslprogress *progress)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
saslstate newstate = SASL_FINAL;
struct bufref resp;
const char * const hostname = SSL_HOST_NAME();
Expand Down Expand Up @@ -574,7 +595,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
result = Curl_auth_create_digest_md5_message(data, &serverdata,
conn->user, conn->passwd,
service, &resp);
newstate = SASL_DIGESTMD5_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. */
Expand Down Expand Up @@ -687,7 +709,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
sasl->authmechs ^= sasl->authused;

/* Start an alternative SASL authentication */
return Curl_sasl_start(sasl, data, conn, sasl->force_ir, progress);
return Curl_sasl_start(sasl, data, sasl->force_ir, progress);
default:
failf(data, "Unsupported SASL authentication mechanism");
result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */
Expand All @@ -699,14 +721,13 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
switch(result) {
case CURLE_BAD_CONTENT_ENCODING:
/* Cancel dialog */
result = sasl->params->sendcont(data, conn, "*");
result = sasl->params->cancelauth(data, sasl->curmech);
newstate = SASL_CANCEL;
break;
case CURLE_OK:
result = build_message(data, &resp);
result = build_message(sasl, data, &resp);
if(!result)
result = sasl->params->sendcont(data, conn,
(const char *) Curl_bufref_ptr(&resp));
result = sasl->params->contauth(data, sasl->curmech, &resp);
break;
default:
newstate = SASL_STOP; /* Stop on error */
Expand Down
64 changes: 36 additions & 28 deletions lib/curl_sasl.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

#include <curl/curl.h>

#include "bufref.h"

struct Curl_easy;
struct connectdata;

Expand All @@ -46,17 +48,20 @@ struct connectdata;
#define SASL_AUTH_DEFAULT (SASL_AUTH_ANY & ~SASL_MECH_EXTERNAL)

/* Authentication mechanism strings */
#define SASL_MECH_STRING_LOGIN "LOGIN"
#define SASL_MECH_STRING_PLAIN "PLAIN"
#define SASL_MECH_STRING_CRAM_MD5 "CRAM-MD5"
#define SASL_MECH_STRING_DIGEST_MD5 "DIGEST-MD5"
#define SASL_MECH_STRING_GSSAPI "GSSAPI"
#define SASL_MECH_STRING_EXTERNAL "EXTERNAL"
#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"
#define SASL_MECH_STRING_LOGIN "LOGIN"
#define SASL_MECH_STRING_PLAIN "PLAIN"
#define SASL_MECH_STRING_CRAM_MD5 "CRAM-MD5"
#define SASL_MECH_STRING_DIGEST_MD5 "DIGEST-MD5"
#define SASL_MECH_STRING_GSSAPI "GSSAPI"
#define SASL_MECH_STRING_EXTERNAL "EXTERNAL"
#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 flags */
#define SASL_FLAG_BASE64 0x0001 /* Messages are base64-encoded */

/* SASL machine states */
typedef enum {
Expand Down Expand Up @@ -90,30 +95,34 @@ typedef enum {
/* Protocol dependent SASL parameters */
struct SASLproto {
const char *service; /* The service name */
int contcode; /* Code to receive when continuation is expected */
int finalcode; /* Code to receive upon authentication success */
size_t maxirlen; /* Maximum initial response length */
CURLcode (*sendauth)(struct Curl_easy *data,
struct connectdata *conn,
const char *mech, const char *ir);
CURLcode (*sendauth)(struct Curl_easy *data, const char *mech,
const struct bufref *ir);
/* Send authentication command */
CURLcode (*sendcont)(struct Curl_easy *data,
struct connectdata *conn, const char *contauth);
CURLcode (*contauth)(struct Curl_easy *data, const char *mech,
const struct bufref *contauth);
/* Send authentication continuation */
void (*getmessage)(char *buffer, char **outptr);
CURLcode (*cancelauth)(struct Curl_easy *data, const char *mech);
/* Cancel authentication. */
CURLcode (*getmessage)(struct Curl_easy *data, struct bufref *out);
/* Get SASL response message */
size_t maxirlen; /* Maximum initial response length */
int contcode; /* Code to receive when continuation is expected */
int finalcode; /* Code to receive upon authentication success */
unsigned short defmechs; /* Mechanisms enabled by default */
unsigned short flags; /* Configuration flags. */
};

/* Per-connection parameters */
struct SASL {
const struct SASLproto *params; /* Protocol dependent parameters */
saslstate state; /* Current machine state */
saslstate state; /* Current machine state */
const char *curmech; /* Current mechanism id. */
unsigned short authmechs; /* Accepted authentication mechanisms */
unsigned short prefmech; /* Preferred authentication mechanism */
unsigned short authused; /* Auth mechanism used for the connection */
bool resetprefs; /* For URL auth option parsing. */
bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */
bool force_ir; /* Protocol always supports initial response */
bool resetprefs; /* For URL auth option parsing. */
bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */
bool force_ir; /* Protocol always supports initial response */
};

/* This is used to test whether the line starts with the given mechanism */
Expand All @@ -123,7 +132,7 @@ struct SASL {

/* This is used to cleanup any libraries or curl modules used by the sasl
functions */
void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused);
void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused);

/* Convert a mechanism name to a token */
unsigned short Curl_sasl_decode_mech(const char *ptr,
Expand All @@ -134,19 +143,18 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
const char *value, size_t len);

/* Initializes an SASL structure */
void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params);
void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data,
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 */
CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
struct connectdata *conn,
bool force_ir, saslprogress *progress);

/* Continue an SASL authentication */
CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
struct connectdata *conn,
int code, saslprogress *progress);

#endif /* HEADER_CURL_SASL_H */
Loading

0 comments on commit 3664994

Please sign in to comment.