mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2026-08-08 16:48:06 +00:00
Handle SASL initialisation mainly in red-stream.c
Asynchronous code jumping from a file to another is tedious to read also having code handling the same stuff in two files does not look a good design. Signed-off-by: Frediano Ziglio <fziglio@redhat.com> Acked-by: Christophe Fergeau <cfergeau@redhat.com>
This commit is contained in:
parent
6543bef0cb
commit
5c438510cd
@ -732,6 +732,36 @@ static int auth_sasl_check_ssf(RedSASL *sasl, int *runSSF)
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef struct RedSASLAuth {
|
||||
RedStream *stream;
|
||||
// callback to call if success
|
||||
RedSaslResult result_cb;
|
||||
void *result_opaque;
|
||||
// saved Async callback, we need to call if failed as
|
||||
// we need to chain it in order to use a different opaque data
|
||||
AsyncReadError saved_error_cb;
|
||||
} RedSASLAuth;
|
||||
|
||||
// handle SASL termination, either success or error
|
||||
// NOTE: After this function is called usually there should be a
|
||||
// return or the function should exit
|
||||
static void red_sasl_async_result(RedSASLAuth *auth, RedSaslError err)
|
||||
{
|
||||
red_stream_set_async_error_handler(auth->stream, auth->saved_error_cb);
|
||||
auth->result_cb(auth->result_opaque, err);
|
||||
g_free(auth);
|
||||
}
|
||||
|
||||
static void red_sasl_error(void *opaque, int err)
|
||||
{
|
||||
RedSASLAuth *auth = opaque;
|
||||
red_stream_set_async_error_handler(auth->stream, auth->saved_error_cb);
|
||||
if (auth->saved_error_cb) {
|
||||
auth->saved_error_cb(auth->result_opaque, err);
|
||||
}
|
||||
g_free(auth);
|
||||
}
|
||||
|
||||
/*
|
||||
* Step Msg
|
||||
*
|
||||
@ -749,8 +779,11 @@ static int auth_sasl_check_ssf(RedSASL *sasl, int *runSSF)
|
||||
#define SASL_MAX_MECHNAME_LEN 100
|
||||
#define SASL_DATA_MAX_LEN (1024 * 1024)
|
||||
|
||||
RedSaslError red_sasl_handle_auth_step(RedStream *stream, AsyncReadDone read_cb, void *opaque)
|
||||
static void red_sasl_handle_auth_steplen(void *opaque);
|
||||
|
||||
static void red_sasl_handle_auth_step(void *opaque)
|
||||
{
|
||||
RedStream *stream = ((RedSASLAuth *)opaque)->stream;
|
||||
const char *serverout;
|
||||
unsigned int serveroutlen;
|
||||
int err;
|
||||
@ -776,13 +809,13 @@ RedSaslError red_sasl_handle_auth_step(RedStream *stream, AsyncReadDone read_cb,
|
||||
err != SASL_CONTINUE) {
|
||||
spice_warning("sasl step failed %d (%s)",
|
||||
err, sasl_errdetail(sasl->conn));
|
||||
return RED_SASL_ERROR_GENERIC;
|
||||
return red_sasl_async_result(opaque, RED_SASL_ERROR_GENERIC);
|
||||
}
|
||||
|
||||
if (serveroutlen > SASL_DATA_MAX_LEN) {
|
||||
spice_warning("sasl step reply data too long %d",
|
||||
serveroutlen);
|
||||
return RED_SASL_ERROR_INVALID_DATA;
|
||||
return red_sasl_async_result(opaque, RED_SASL_ERROR_GENERIC);
|
||||
}
|
||||
|
||||
spice_debug("SASL return data %d bytes, %p", serveroutlen, serverout);
|
||||
@ -802,8 +835,8 @@ RedSaslError red_sasl_handle_auth_step(RedStream *stream, AsyncReadDone read_cb,
|
||||
spice_debug("%s", "Authentication must continue (step)");
|
||||
/* Wait for step length */
|
||||
red_stream_async_read(stream, (uint8_t *)&sasl->len, sizeof(uint32_t),
|
||||
read_cb, opaque);
|
||||
return RED_SASL_ERROR_CONTINUE;
|
||||
red_sasl_handle_auth_steplen, opaque);
|
||||
return;
|
||||
} else {
|
||||
int ssf;
|
||||
|
||||
@ -821,7 +854,7 @@ RedSaslError red_sasl_handle_auth_step(RedStream *stream, AsyncReadDone read_cb,
|
||||
sasl->runSSF = ssf;
|
||||
red_stream_disable_writev(stream); /* make sure writev isn't called directly anymore */
|
||||
|
||||
return RED_SASL_ERROR_OK;
|
||||
return red_sasl_async_result(opaque, RED_SASL_ERROR_OK);
|
||||
}
|
||||
|
||||
authreject:
|
||||
@ -829,31 +862,26 @@ authreject:
|
||||
red_stream_write_u32(stream, sizeof("Authentication failed"));
|
||||
red_stream_write_all(stream, "Authentication failed", sizeof("Authentication failed"));
|
||||
|
||||
return RED_SASL_ERROR_AUTH_FAILED;
|
||||
red_sasl_async_result(opaque, RED_SASL_ERROR_AUTH_FAILED);
|
||||
}
|
||||
|
||||
RedSaslError red_sasl_handle_auth_steplen(RedStream *stream, AsyncReadDone read_cb, void *opaque)
|
||||
static void red_sasl_handle_auth_steplen(void *opaque)
|
||||
{
|
||||
RedStream *stream = ((RedSASLAuth *)opaque)->stream;
|
||||
RedSASL *sasl = &stream->priv->sasl;
|
||||
|
||||
spice_debug("Got steplen %d", sasl->len);
|
||||
if (sasl->len > SASL_DATA_MAX_LEN) {
|
||||
spice_warning("Too much SASL data %d", sasl->len);
|
||||
return RED_SASL_ERROR_INVALID_DATA;
|
||||
return red_sasl_async_result(opaque, RED_SASL_ERROR_GENERIC);
|
||||
}
|
||||
|
||||
if (sasl->len == 0) {
|
||||
read_cb(opaque);
|
||||
/* FIXME: can't report potential errors correctly here,
|
||||
* but read_cb() will have done the needed RedLinkInfo cleanups
|
||||
* if an error occurs, so the caller should not need to do more
|
||||
* treatment */
|
||||
return RED_SASL_ERROR_OK;
|
||||
red_sasl_handle_auth_step(opaque);
|
||||
} else {
|
||||
sasl->data = g_realloc(sasl->data, sasl->len);
|
||||
red_stream_async_read(stream, (uint8_t *)sasl->data, sasl->len,
|
||||
read_cb, opaque);
|
||||
return RED_SASL_ERROR_OK;
|
||||
red_sasl_handle_auth_step, opaque);
|
||||
}
|
||||
}
|
||||
|
||||
@ -872,8 +900,9 @@ RedSaslError red_sasl_handle_auth_steplen(RedStream *stream, AsyncReadDone read_
|
||||
* u8 continue
|
||||
*/
|
||||
|
||||
RedSaslError red_sasl_handle_auth_start(RedStream *stream, AsyncReadDone read_cb, void *opaque)
|
||||
static void red_sasl_handle_auth_start(void *opaque)
|
||||
{
|
||||
RedStream *stream = ((RedSASLAuth *)opaque)->stream;
|
||||
const char *serverout;
|
||||
unsigned int serveroutlen;
|
||||
int err;
|
||||
@ -900,13 +929,13 @@ RedSaslError red_sasl_handle_auth_start(RedStream *stream, AsyncReadDone read_cb
|
||||
err != SASL_CONTINUE) {
|
||||
spice_warning("sasl start failed %d (%s)",
|
||||
err, sasl_errdetail(sasl->conn));
|
||||
return RED_SASL_ERROR_INVALID_DATA;
|
||||
return red_sasl_async_result(opaque, RED_SASL_ERROR_GENERIC);
|
||||
}
|
||||
|
||||
if (serveroutlen > SASL_DATA_MAX_LEN) {
|
||||
spice_warning("sasl start reply data too long %d",
|
||||
serveroutlen);
|
||||
return RED_SASL_ERROR_INVALID_DATA;
|
||||
serveroutlen);
|
||||
return red_sasl_async_result(opaque, RED_SASL_ERROR_GENERIC);
|
||||
}
|
||||
|
||||
spice_debug("SASL return data %d bytes, %p", serveroutlen, serverout);
|
||||
@ -926,8 +955,8 @@ RedSaslError red_sasl_handle_auth_start(RedStream *stream, AsyncReadDone read_cb
|
||||
spice_debug("%s", "Authentication must continue (start)");
|
||||
/* Wait for step length */
|
||||
red_stream_async_read(stream, (uint8_t *)&sasl->len, sizeof(uint32_t),
|
||||
read_cb, opaque);
|
||||
return RED_SASL_ERROR_CONTINUE;
|
||||
red_sasl_handle_auth_steplen, opaque);
|
||||
return;
|
||||
} else {
|
||||
int ssf;
|
||||
|
||||
@ -944,7 +973,8 @@ RedSaslError red_sasl_handle_auth_start(RedStream *stream, AsyncReadDone read_cb
|
||||
*/
|
||||
sasl->runSSF = ssf;
|
||||
red_stream_disable_writev(stream); /* make sure writev isn't called directly anymore */
|
||||
return RED_SASL_ERROR_OK;
|
||||
|
||||
return red_sasl_async_result(opaque, RED_SASL_ERROR_OK);
|
||||
}
|
||||
|
||||
authreject:
|
||||
@ -952,32 +982,32 @@ authreject:
|
||||
red_stream_write_u32(stream, sizeof("Authentication failed"));
|
||||
red_stream_write_all(stream, "Authentication failed", sizeof("Authentication failed"));
|
||||
|
||||
return RED_SASL_ERROR_AUTH_FAILED;
|
||||
red_sasl_async_result(opaque, RED_SASL_ERROR_AUTH_FAILED);
|
||||
}
|
||||
|
||||
RedSaslError red_sasl_handle_auth_startlen(RedStream *stream, AsyncReadDone read_cb, void *opaque)
|
||||
static void red_sasl_handle_auth_startlen(void *opaque)
|
||||
{
|
||||
RedStream *stream = ((RedSASLAuth *)opaque)->stream;
|
||||
RedSASL *sasl = &stream->priv->sasl;
|
||||
|
||||
spice_debug("Got client start len %d", sasl->len);
|
||||
if (sasl->len > SASL_DATA_MAX_LEN) {
|
||||
spice_warning("Too much SASL data %d", sasl->len);
|
||||
return RED_SASL_ERROR_INVALID_DATA;
|
||||
return red_sasl_async_result(opaque, RED_SASL_ERROR_INVALID_DATA);
|
||||
}
|
||||
|
||||
if (sasl->len == 0) {
|
||||
return RED_SASL_ERROR_RETRY;
|
||||
return red_sasl_handle_auth_start(opaque);
|
||||
}
|
||||
|
||||
sasl->data = g_realloc(sasl->data, sasl->len);
|
||||
red_stream_async_read(stream, (uint8_t *)sasl->data, sasl->len,
|
||||
read_cb, opaque);
|
||||
|
||||
return RED_SASL_ERROR_OK;
|
||||
red_sasl_handle_auth_start, opaque);
|
||||
}
|
||||
|
||||
bool red_sasl_handle_auth_mechname(RedStream *stream, AsyncReadDone read_cb, void *opaque)
|
||||
static void red_sasl_handle_auth_mechname(void *opaque)
|
||||
{
|
||||
RedStream *stream = ((RedSASLAuth *)opaque)->stream;
|
||||
RedSASL *sasl = &stream->priv->sasl;
|
||||
|
||||
sasl->mechname[sasl->len] = '\0';
|
||||
@ -988,36 +1018,33 @@ bool red_sasl_handle_auth_mechname(RedStream *stream, AsyncReadDone read_cb, voi
|
||||
sprintf(quoted_mechname, ",%s,", sasl->mechname);
|
||||
|
||||
if (strchr(sasl->mechname, ',') || strstr(sasl->mechlist, quoted_mechname) == NULL) {
|
||||
return false;
|
||||
return red_sasl_async_result(opaque, RED_SASL_ERROR_INVALID_DATA);
|
||||
}
|
||||
|
||||
spice_debug("Validated mechname '%s'", sasl->mechname);
|
||||
|
||||
red_stream_async_read(stream, (uint8_t *)&sasl->len, sizeof(uint32_t),
|
||||
read_cb, opaque);
|
||||
|
||||
return true;
|
||||
red_sasl_handle_auth_startlen, opaque);
|
||||
}
|
||||
|
||||
bool red_sasl_handle_auth_mechlen(RedStream *stream, AsyncReadDone read_cb, void *opaque)
|
||||
static void red_sasl_handle_auth_mechlen(void *opaque)
|
||||
{
|
||||
RedStream *stream = ((RedSASLAuth *)opaque)->stream;
|
||||
RedSASL *sasl = &stream->priv->sasl;
|
||||
|
||||
if (sasl->len < 1 || sasl->len > SASL_MAX_MECHNAME_LEN) {
|
||||
spice_warning("Got bad client mechname len %d", sasl->len);
|
||||
return false;
|
||||
return red_sasl_async_result(opaque, RED_SASL_ERROR_GENERIC);
|
||||
}
|
||||
|
||||
sasl->mechname = g_malloc(sasl->len + 1);
|
||||
|
||||
spice_debug("Wait for client mechname");
|
||||
red_stream_async_read(stream, (uint8_t *)sasl->mechname, sasl->len,
|
||||
read_cb, opaque);
|
||||
|
||||
return true;
|
||||
red_sasl_handle_auth_mechname, opaque);
|
||||
}
|
||||
|
||||
bool red_sasl_start_auth(RedStream *stream, AsyncReadDone read_cb, void *opaque)
|
||||
bool red_sasl_start_auth(RedStream *stream, RedSaslResult result_cb, void *result_opaque)
|
||||
{
|
||||
const char *mechlist = NULL;
|
||||
sasl_security_properties_t secprops;
|
||||
@ -1025,6 +1052,7 @@ bool red_sasl_start_auth(RedStream *stream, AsyncReadDone read_cb, void *opaque)
|
||||
char *localAddr, *remoteAddr;
|
||||
int mechlistlen;
|
||||
RedSASL *sasl = &stream->priv->sasl;
|
||||
RedSASLAuth *auth;
|
||||
|
||||
if (!(localAddr = red_stream_get_local_address(stream))) {
|
||||
goto error;
|
||||
@ -1119,9 +1147,16 @@ bool red_sasl_start_auth(RedStream *stream, AsyncReadDone read_cb, void *opaque)
|
||||
goto error;
|
||||
}
|
||||
|
||||
auth = g_new0(RedSASLAuth, 1);
|
||||
auth->stream = stream;
|
||||
auth->result_cb = result_cb;
|
||||
auth->result_opaque = result_opaque;
|
||||
auth->saved_error_cb = stream->priv->async_read.error;
|
||||
red_stream_set_async_error_handler(stream, red_sasl_error);
|
||||
|
||||
spice_debug("Wait for client mechname length");
|
||||
red_stream_async_read(stream, (uint8_t *)&sasl->len, sizeof(uint32_t),
|
||||
read_cb, opaque);
|
||||
red_sasl_handle_auth_mechlen, auth);
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
@ -73,17 +73,10 @@ typedef enum {
|
||||
RED_SASL_ERROR_OK,
|
||||
RED_SASL_ERROR_GENERIC,
|
||||
RED_SASL_ERROR_INVALID_DATA,
|
||||
RED_SASL_ERROR_RETRY,
|
||||
RED_SASL_ERROR_CONTINUE,
|
||||
RED_SASL_ERROR_AUTH_FAILED
|
||||
} RedSaslError;
|
||||
|
||||
RedSaslError red_sasl_handle_auth_step(RedStream *stream, AsyncReadDone read_cb, void *opaque);
|
||||
RedSaslError red_sasl_handle_auth_steplen(RedStream *stream, AsyncReadDone read_cb, void *opaque);
|
||||
RedSaslError red_sasl_handle_auth_start(RedStream *stream, AsyncReadDone read_cb, void *opaque);
|
||||
RedSaslError red_sasl_handle_auth_startlen(RedStream *stream, AsyncReadDone read_cb, void *opaque);
|
||||
bool red_sasl_handle_auth_mechname(RedStream *stream, AsyncReadDone read_cb, void *opaque);
|
||||
bool red_sasl_handle_auth_mechlen(RedStream *stream, AsyncReadDone read_cb, void *opaque);
|
||||
bool red_sasl_start_auth(RedStream *stream, AsyncReadDone read_cb, void *opaque);
|
||||
typedef void (*RedSaslResult)(void *opaque, RedSaslError err);
|
||||
bool red_sasl_start_auth(RedStream *stream, RedSaslResult result_cb, void *opaque);
|
||||
|
||||
#endif /* RED_STREAM_H_ */
|
||||
|
||||
119
server/reds.c
119
server/reds.c
@ -2100,123 +2100,30 @@ static void reds_get_spice_ticket(RedLinkInfo *link)
|
||||
}
|
||||
|
||||
#if HAVE_SASL
|
||||
/*
|
||||
* Step Msg
|
||||
*
|
||||
* Input from client:
|
||||
*
|
||||
* u32 clientin-length
|
||||
* u8-array clientin-string
|
||||
*
|
||||
* Output to client:
|
||||
*
|
||||
* u32 serverout-length
|
||||
* u8-array serverout-strin
|
||||
* u8 continue
|
||||
*/
|
||||
|
||||
static void reds_handle_auth_sasl_steplen(void *opaque);
|
||||
|
||||
static void reds_handle_auth_sasl_step(void *opaque)
|
||||
static void reds_handle_sasl_result(void *opaque, RedSaslError status)
|
||||
{
|
||||
RedLinkInfo *link = (RedLinkInfo *)opaque;
|
||||
RedSaslError status;
|
||||
|
||||
status = red_sasl_handle_auth_step(link->stream, reds_handle_auth_sasl_steplen, link);
|
||||
if (status == RED_SASL_ERROR_OK) {
|
||||
reds_handle_link(link);
|
||||
} else if (status != RED_SASL_ERROR_CONTINUE) {
|
||||
reds_link_free(link);
|
||||
}
|
||||
}
|
||||
|
||||
static void reds_handle_auth_sasl_steplen(void *opaque)
|
||||
{
|
||||
RedLinkInfo *link = (RedLinkInfo *)opaque;
|
||||
RedSaslError status;
|
||||
|
||||
status = red_sasl_handle_auth_steplen(link->stream, reds_handle_auth_sasl_step, link);
|
||||
if (status != RED_SASL_ERROR_OK) {
|
||||
reds_link_free(link);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Start Msg
|
||||
*
|
||||
* Input from client:
|
||||
*
|
||||
* u32 clientin-length
|
||||
* u8-array clientin-string
|
||||
*
|
||||
* Output to client:
|
||||
*
|
||||
* u32 serverout-length
|
||||
* u8-array serverout-strin
|
||||
* u8 continue
|
||||
*/
|
||||
|
||||
|
||||
static void reds_handle_auth_sasl_start(void *opaque)
|
||||
{
|
||||
RedLinkInfo *link = (RedLinkInfo *)opaque;
|
||||
RedSaslError status;
|
||||
|
||||
status = red_sasl_handle_auth_start(link->stream, reds_handle_auth_sasl_steplen, link);
|
||||
if (status == RED_SASL_ERROR_OK) {
|
||||
reds_handle_link(link);
|
||||
} else if (status != RED_SASL_ERROR_CONTINUE) {
|
||||
reds_link_free(link);
|
||||
}
|
||||
}
|
||||
|
||||
static void reds_handle_auth_startlen(void *opaque)
|
||||
{
|
||||
RedLinkInfo *link = (RedLinkInfo *)opaque;
|
||||
RedSaslError status;
|
||||
|
||||
status = red_sasl_handle_auth_startlen(link->stream, reds_handle_auth_sasl_start, link);
|
||||
switch (status) {
|
||||
case RED_SASL_ERROR_OK:
|
||||
break;
|
||||
case RED_SASL_ERROR_RETRY:
|
||||
reds_handle_auth_sasl_start(opaque);
|
||||
break;
|
||||
case RED_SASL_ERROR_GENERIC:
|
||||
case RED_SASL_ERROR_INVALID_DATA:
|
||||
reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
|
||||
reds_link_free(link);
|
||||
break;
|
||||
default:
|
||||
g_warn_if_reached();
|
||||
reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
|
||||
reds_link_free(link);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void reds_handle_auth_mechname(void *opaque)
|
||||
{
|
||||
RedLinkInfo *link = (RedLinkInfo *)opaque;
|
||||
|
||||
if (!red_sasl_handle_auth_mechname(link->stream, reds_handle_auth_startlen, link)) {
|
||||
reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
|
||||
case RED_SASL_ERROR_OK:
|
||||
reds_handle_link(link);
|
||||
break;
|
||||
case RED_SASL_ERROR_INVALID_DATA:
|
||||
reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
|
||||
reds_link_free(link);
|
||||
}
|
||||
}
|
||||
|
||||
static void reds_handle_auth_mechlen(void *opaque)
|
||||
{
|
||||
RedLinkInfo *link = (RedLinkInfo *)opaque;
|
||||
|
||||
if (!red_sasl_handle_auth_mechlen(link->stream, reds_handle_auth_mechname, link)) {
|
||||
break;
|
||||
default:
|
||||
// in these cases error was reported using SASL protocol
|
||||
// (RED_SASL_ERROR_AUTH_FAILED) or we just need to close the
|
||||
// connection
|
||||
reds_link_free(link);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void reds_start_auth_sasl(RedLinkInfo *link)
|
||||
{
|
||||
if (!red_sasl_start_auth(link->stream, reds_handle_auth_mechlen, link)) {
|
||||
if (!red_sasl_start_auth(link->stream, reds_handle_sasl_result, link)) {
|
||||
reds_link_free(link);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user