GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / lib / server.c
index 4cdf456..c0ee30f 100644 (file)
@@ -1025,6 +1025,7 @@ static int mech_permitted(sasl_conn_t *conn,
     if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
             == SASL_OK) {
        const char *mlist = NULL;
+       int plus = 0;
 
        getopt(context, NULL, "mech_list", &mlist, NULL);
 
@@ -1034,9 +1035,8 @@ static int mech_permitted(sasl_conn_t *conn,
 
            while (*mlist) {
                for (cp = mlist; *cp && !isspace((int) *cp); cp++);
-               if (((size_t) (cp - mlist) == strlen(plug->mech_name)) &&
-                   !strncasecmp(mlist, plug->mech_name,
-                                strlen(plug->mech_name))) {
+               if (_sasl_is_equal_mech(mlist, plug->mech_name, (size_t) (cp - mlist), &plus)) {
+                   /* found a match */
                    break;
                }
                mlist = cp;
@@ -1214,6 +1214,7 @@ int sasl_server_start(sasl_conn_t *conn,
     int result;
     context_list_t *cur, **prev;
     mechanism_t *m;
+    size_t mech_len;
     int plus = 0;
 
     if (_sasl_server_active==0) return SASL_NOTINIT;
@@ -1221,6 +1222,7 @@ int sasl_server_start(sasl_conn_t *conn,
     /* make sure mech is valid mechanism
        if not return appropriate error */
     m=mechlist->mech_list;
+    mech_len = strlen(mech);
 
     /* check parameters */
     if(!conn) return SASL_BADPARAM;
@@ -1232,7 +1234,7 @@ int sasl_server_start(sasl_conn_t *conn,
     if(serveroutlen) *serveroutlen = 0;
 
     while (m != NULL) {
-       if (_sasl_is_equal_mech(mech, m->m.plug->mech_name, &plus))
+       if (_sasl_is_equal_mech(mech, m->m.plug->mech_name, mech_len, &plus))
            break;
 
        m = m->next;
@@ -1454,17 +1456,38 @@ int sasl_server_step(sasl_conn_t *conn,
        }
 
         /* Validate channel bindings */
-        if (conn->oparams.chanbindingflag == SASL_CB_FLAG_NONE &&
-            s_conn->sparams->chanbindingcrit) {
-           sasl_seterror(conn, 0,
-                         "server requires channel binding but client provided none");
-            ret = SASL_BADAUTH;
-        } else if (conn->oparams.chanbindingflag == SASL_CB_FLAG_WANT &&
-            SASL_CB_PRESENT(s_conn->sparams)) {
-            sasl_seterror(conn, 0,
-                          "client incorrectly determined server had no channel binding");
-            ret = SASL_BADAUTH;
-        } else if (conn->oparams.user == NULL || conn->oparams.authid == NULL) {
+       switch (conn->oparams.cbindingdisp) {
+       case SASL_CB_DISP_NONE:
+           if (SASL_CB_CRITICAL(s_conn->sparams)) {
+               sasl_seterror(conn, 0,
+                             "server requires channel binding but client provided none");
+               ret = SASL_BADBINDING;
+           }
+           break;
+       case SASL_CB_DISP_WANT:
+           if (SASL_CB_PRESENT(s_conn->sparams)) {
+               sasl_seterror(conn, 0,
+                             "client incorrectly assumed server had no channel binding");
+               ret = SASL_BADAUTH;
+           }
+           break;
+       case SASL_CB_DISP_USED:
+           if (!SASL_CB_PRESENT(s_conn->sparams)) {
+               sasl_seterror(conn, 0,
+                             "client provided channel binding but server had none");
+               ret = SASL_BADBINDING;
+           } else if (strcmp(conn->oparams.cbindingname,
+                      s_conn->sparams->cbinding->name) != 0) {
+               sasl_seterror(conn, 0,
+                             "client channel binding %s does not match server %s",
+                             conn->oparams.cbindingname, s_conn->sparams->cbinding->name);
+               ret = SASL_BADBINDING;
+           }
+           break;
+       }
+
+        if (ret == SASL_OK &&
+           (conn->oparams.user == NULL || conn->oparams.authid == NULL)) {
            sasl_seterror(conn, 0,
                          "mech did not call canon_user for both authzid " \
                          "and authid");
@@ -1567,25 +1590,35 @@ int _sasl_server_listmech(sasl_conn_t *conn,
   for (lup = 0; lup < mechlist->mech_length; lup++) {
       /* currently, we don't use the "user" parameter for anything */
       if (mech_permitted(conn, listptr) == SASL_OK) {
-         if (pcount != NULL)
+          /*
+           * If the server would never succeed in the authentication of
+           * he non-PLUS-variant due to policy reasons, it MUST advertise
+           * only the PLUS-variant.
+           */
+          if (!SASL_CB_PRESENT(s_conn->sparams) ||
+              !SASL_CB_CRITICAL(s_conn->sparams)) {
+            if (pcount != NULL)
              (*pcount)++;
-
-         /* print separator */
-         if (flag) {
-             strcat(conn->mechlist_buf, mysep);
-         } else {
-             flag = 1;
-         }
-
-         /* now print the mechanism name */
-         strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
-
-         /* advertise -PLUS variant if mechanism and application support CB */
+            if (flag)
+              strcat(conn->mechlist_buf, mysep);
+            else
+              flag = 1;
+           strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
+          }
+          /*
+           * If the server cannot support channel binding, it SHOULD
+           * advertise only the non-PLUS-variant. Here, supporting channel
+           * binding means the underlying SASL mechanism supports it and
+           * the application has set some channel binding data.
+           */
          if ((listptr->m.plug->features & SASL_FEAT_CHANNEL_BINDING) &&
              SASL_CB_PRESENT(s_conn->sparams)) {
            if (pcount != NULL)
                (*pcount)++;
-           strcat(conn->mechlist_buf, mysep);
+            if (flag)
+              strcat(conn->mechlist_buf, mysep);
+            else
+              flag = 1;
            strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
            strcat(conn->mechlist_buf, "-PLUS");
          }