[Spice-devel] [PATCH spice-server v6 6/9] Handle SASL initialisation mainly in red-stream.c

Frediano Ziglio fziglio at redhat.com
Tue Jan 9 07:45:02 UTC 2018


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 at redhat.com>
---
 server/red-stream.c | 121 +++++++++++++++++++++++++++++++++-------------------
 server/red-stream.h |  11 +----
 server/reds.c       | 119 ++++++---------------------------------------------
 3 files changed, 93 insertions(+), 158 deletions(-)

diff --git a/server/red-stream.c b/server/red-stream.c
index fccad8b2..0176f0a9 100644
--- a/server/red-stream.c
+++ b/server/red-stream.c
@@ -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;
 
diff --git a/server/red-stream.h b/server/red-stream.h
index a8d855c2..4d5075ed 100644
--- a/server/red-stream.h
+++ b/server/red-stream.h
@@ -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_ */
diff --git a/server/reds.c b/server/reds.c
index 9338b78b..777f5ba4 100644
--- a/server/reds.c
+++ b/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)
-{
-    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)
+static void reds_handle_sasl_result(void *opaque, RedSaslError status)
 {
     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);
     }
 }
-- 
2.14.3



More information about the Spice-devel mailing list