make channel binding disposition an enum
[cyrus-sasl.git] / lib / client.c
index 9f3e594..e4ef163 100644 (file)
@@ -185,7 +185,7 @@ client_idle(sasl_conn_t *conn)
 /* initialize the SASL client drivers
  *  callbacks      -- base callbacks for all client connections
  * returns:
- *  SASL_OK    -- Success
+ *  SASL_OK        -- Success
  *  SASL_NOMEM     -- Not enough memory
  *  SASL_BADVERS   -- Mechanism version mismatch
  *  SASL_BADPARAM  -- error in config file
@@ -409,33 +409,45 @@ _sasl_client_order_mechs(const sasl_utils_t *utils,
                         int *server_can_cb)
 {
     char *list, *listp;
-    size_t i;
-    const char *p, *start  = NULL;
+    size_t i, mechslen, start;
 
     *count = 0;
     *server_can_cb = 0;
 
-    listp = list = utils->malloc(strlen(mechs) + 1);
+    if (mechs == NULL || mechs[0] == '\0')
+        return SASL_NOMECH;
+
+    mechslen = strlen(mechs);
+
+    listp = list = utils->malloc(mechslen + 1);
     if (list == NULL)
        return SASL_NOMEM;
 
+    /* xxx confirm this with rfc 2222
+     * SASL mechanism allowable characters are "AZaz-_"
+     * seperators can be any other characters and of any length
+     * even variable lengths between
+     *
+     * Apps should be encouraged to simply use space or comma space
+     * though
+     */
+#define ismechchar(c)   (isalnum((c)) || (c) == '_' || (c) == '-')
     do {
-       for (start = p = mechs, i = 0; *p != '\0'; p++) {
-           if (isspace(*p) || p[1] == '\0') {
-               size_t len = p - start;
-
-               if (p[1] == '\0')
-                   len++;
-
-               if (_mech_plus_p(start, len) == has_cb_data) {
-                   memcpy(listp, start, len);
+        for (i = start = 0; i <= mechslen; i++) {
+           if (!ismechchar(mechs[i])) {
+                const char *mechp = &mechs[start];
+               size_t len = i - start;
+
+               if (len != 0 &&
+                    _mech_plus_p(mechp, len) == has_cb_data) {
+                   memcpy(listp, mechp, len);
                    listp[len] = '\0';
                    listp += len + 1;
                    (*count)++;
                    if (*server_can_cb == 0 && has_cb_data)
                        *server_can_cb = 1;
                }
-               start = p + 1;
+               start = ++i;
            }
        }
        if (has_cb_data)
@@ -444,19 +456,55 @@ _sasl_client_order_mechs(const sasl_utils_t *utils,
            break;
     } while (1);
 
-    *listp = '\0';
+    if (*count == 0) {
+        utils->free(list);
+        return SASL_NOMECH;
+    }
+
     *ordered_mechs = list;
 
     return SASL_OK;
 }
 
+static inline int
+_sasl_cbinding_disp(sasl_client_params_t *cparams,
+                    int mech_nego,
+                    int server_can_cb,
+                    sasl_cbinding_disp_t *cbindingdisp)
+{
+    /*
+     * If negotiating mechanisms, then we fail immediately if the
+     * client requires channel binding and the server does not
+     * advertise support. Otherwise we send "y" (which later will
+     * become "p" if we select a supporting mechanism).
+     *
+     * If the client explicitly selected a mechanism, then we only
+     * send channel bindings if they're marked critical.
+     */
+
+    *cbindingdisp = SASL_CB_DISP_NONE;
+
+    if (SASL_CB_PRESENT(cparams)) {
+        if (mech_nego) {
+            if (!server_can_cb && SASL_CB_CRITICAL(cparams))
+               return SASL_NOMECH;
+            else
+                *cbindingdisp = SASL_CB_DISP_WANT;
+        } else if (SASL_CB_CRITICAL(cparams)) {
+            *cbindingdisp = SASL_CB_DISP_USED;
+        }
+    }
+
+    return SASL_OK;
+}
+
 /* select a mechanism for a connection
  *  mechlist      -- mechanisms server has available (punctuation ignored)
- *  secret     -- optional secret from previous session
+ *  secret        -- optional secret from previous session
  * output:
  *  prompt_need   -- on SASL_INTERACT, list of prompts needed to continue
  *  clientout     -- the initial client response to send to the server
- *  mech         -- set to mechanism name
+ *  mech          -- set to mechanism name
  *
  * Returns:
  *  SASL_OK       -- success
@@ -465,14 +513,6 @@ _sasl_client_order_mechs(const sasl_utils_t *utils,
  *  SASL_INTERACT -- user interaction needed to fill in prompt_need list
  */
 
-/* xxx confirm this with rfc 2222
- * SASL mechanism allowable characters are "AZaz-_"
- * seperators can be any other characters and of any length
- * even variable lengths between
- *
- * Apps should be encouraged to simply use space or comma space
- * though
- */
 int sasl_client_start(sasl_conn_t *conn,
                      const char *mechlist,
                      sasl_interact_t **prompt_need,
@@ -486,7 +526,7 @@ int sasl_client_start(sasl_conn_t *conn,
     size_t i, list_len;
     sasl_ssf_t bestssf = 0, minssf = 0;
     int result, server_can_cb = 0;
-    unsigned int cbindingdisp = 0;
+    sasl_cbinding_disp_t cbindingdisp;
 
     if(_sasl_client_active==0) return SASL_NOTINIT;
 
@@ -511,6 +551,7 @@ int sasl_client_start(sasl_conn_t *conn,
        minssf = conn->props.min_ssf - conn->external.ssf;
     }
 
+    /* Order mechanisms so -PLUS are preferred */
     result = _sasl_client_order_mechs(c_conn->cparams->utils,
                                      mechlist,
                                      SASL_CB_PRESENT(c_conn->cparams),
@@ -520,16 +561,15 @@ int sasl_client_start(sasl_conn_t *conn,
     if (result != 0)
        goto done;
 
-    if (SASL_CB_PRESENT(c_conn->cparams)) {
-       if (server_can_cb == 0 && SASL_CB_CRITICAL(c_conn->cparams)) {
-           result = SASL_NOMECH;
-           goto done;
-       } else {
-           cbindingdisp = SASL_CB_DISP_WANT;
-       }
-    } else {
-       cbindingdisp = SASL_CB_DISP_NONE;
-    }
+    /*
+     * Determine channel binding disposition based on whether we
+     * are doing mechanism negotiation and whether server supports
+     * channel bindings.
+     */
+    result = _sasl_cbinding_disp(c_conn->cparams, (list_len > 1),
+                                 server_can_cb, &cbindingdisp);
+    if (result != 0)
+       goto done;
 
     for (i = 0, name = ordered_mechs; i < list_len; i++) {
        /* foreach in client list */
@@ -561,7 +601,7 @@ int sasl_client_start(sasl_conn_t *conn,
            }
 
            /* Can we meet it's features? */
-          if (SASL_CB_PRESENT(c_conn->cparams) &&
+           if (cbindingdisp != SASL_CB_DISP_NONE &&
                !(m->m.plug->features & SASL_FEAT_CHANNEL_BINDING)) {
                break;
            }
@@ -615,7 +655,6 @@ int sasl_client_start(sasl_conn_t *conn,
                break;
            }
 
-           /* Prefer server advertised CB mechanisms */
            if (SASL_CB_PRESENT(c_conn->cparams) && plus) {
                cbindingdisp = SASL_CB_DISP_USED;
            }
@@ -662,14 +701,14 @@ int sasl_client_start(sasl_conn_t *conn,
     /* do a step -- but only if we can do a client-send-first */
  dostep:
     if(clientout) {
-       if(c_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) {
-           *clientout = NULL;
-           *clientoutlen = 0;
-           result = SASL_CONTINUE;
-       } else {
-           result = sasl_client_step(conn, NULL, 0, prompt_need,
-                                     clientout, clientoutlen);
-       }
+        if(c_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) {
+            *clientout = NULL;
+            *clientoutlen = 0;
+            result = SASL_CONTINUE;
+        } else {
+            result = sasl_client_step(conn, NULL, 0, prompt_need,
+                                      clientout, clientoutlen);
+        }
     }
     else
        result = SASL_CONTINUE;
@@ -682,13 +721,13 @@ int sasl_client_start(sasl_conn_t *conn,
 
 /* do a single authentication step.
  *  serverin    -- the server message received by the client, MUST have a NUL
- *              sentinel, not counted by serverinlen
+ *                 sentinel, not counted by serverinlen
  * output:
  *  prompt_need -- on SASL_INTERACT, list of prompts needed to continue
  *  clientout   -- the client response to send to the server
  *
  * returns:
- *  SASL_OK    -- success
+ *  SASL_OK        -- success
  *  SASL_INTERACT  -- user interaction needed to fill in prompt_need list
  *  SASL_BADPROT   -- server protocol incorrect/cancelled
  *  SASL_BADSERV   -- server failed mutual auth
@@ -1066,7 +1105,7 @@ int sasl_client_plugin_info (
                m = m->next;
            }
        } else {
-           mech_list = strdup (c_mech_list);
+            mech_list = strdup (c_mech_list);
 
            cur_mech = mech_list;
 
@@ -1092,7 +1131,7 @@ int sasl_client_plugin_info (
                cur_mech = p;
            }
 
-           free (mech_list);
+            free (mech_list);
        }
 
        info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);