GSASL: provide $autnN for scram option expansions
[users/jgh/exim.git] / src / src / auths / gsasl_exim.c
index 7003b0cbb01183b631ce598c6a0f0b8fc4b021b8..80ae354f9ef9dd3a3ab99ec8e07bef1d37cc4d96 100644 (file)
@@ -39,10 +39,6 @@ static void dummy(int x) { dummy2(x-1); }
 #include <gsasl.h>
 #include "gsasl_exim.h"
 
-#ifdef SUPPORT_I18N
-# include <stringprep.h>
-#endif
-
 
 #if GSASL_VERSION_MINOR >= 9
 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
@@ -504,6 +500,36 @@ switch (exim_rc)
 return GSASL_AUTHENTICATION_ERROR;
 }
 
+
+static void
+set_exim_authvar_from_prop(Gsasl_session * sctx, Gsasl_property prop)
+{
+uschar * propval = US gsasl_property_fast(sctx, prop);
+int i = expand_nmax, j = i + 1;
+propval = propval ? string_copy(propval) : US"";
+auth_vars[i] = expand_nstring[j] = propval;
+expand_nlength[j] = Ustrlen(propval);
+expand_nmax = j;
+}
+
+static void
+set_exim_authvars_from_a_az_r_props(Gsasl_session * sctx)
+{
+if (expand_nmax > 0 ) return;
+
+/* Asking for GSASL_AUTHZID calls back into us if we use
+gsasl_property_get(), thus the use of gsasl_property_fast().
+Do we really want to hardcode limits per mechanism?  What happens when
+a new mechanism is added to the library.  It *shouldn't* result in us
+needing to add more glue, since avoiding that is a large part of the
+point of SASL. */
+
+set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
+set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
+set_exim_authvar_from_prop(sctx, GSASL_REALM);
+}
+
+
 static int
 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
   auth_instance *ablock)
@@ -526,15 +552,9 @@ switch (prop)
   case GSASL_VALIDATE_SIMPLE:
     HDEBUG(D_auth) debug_printf(" VALIDATE_SIMPLE\n");
     /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
-    propval = US gsasl_property_fast(sctx, GSASL_AUTHID);
-    auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
-    propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
-    auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US"";
-    propval = US gsasl_property_fast(sctx, GSASL_PASSWORD);
-    auth_vars[2] = expand_nstring[3] = propval ? string_copy(propval) : US"";
-    expand_nmax = 3;
-    for (int i = 1; i <= 3; ++i)
-      expand_nlength[i] = Ustrlen(expand_nstring[i]);
+    set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
+    set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
+    set_exim_authvar_from_prop(sctx, GSASL_PASSWORD);
 
     cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
     checked_server_condition = TRUE;
@@ -548,12 +568,7 @@ switch (prop)
       cbrc = GSASL_AUTHENTICATION_ERROR;
       break;
       }
-    propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
-
-    /* We always set $auth1, even if only to empty string. */
-    auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
-    expand_nlength[1] = Ustrlen(expand_nstring[1]);
-    expand_nmax = 1;
+    set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
 
     cbrc = condition_check(ablock,
        US"server_condition (EXTERNAL)", ablock->server_condition);
@@ -568,13 +583,7 @@ switch (prop)
       cbrc = GSASL_AUTHENTICATION_ERROR;
       break;
       }
-    propval = US gsasl_property_fast(sctx, GSASL_ANONYMOUS_TOKEN);
-
-    /* We always set $auth1, even if only to empty string. */
-
-    auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
-    expand_nlength[1] = Ustrlen(expand_nstring[1]);
-    expand_nmax = 1;
+    set_exim_authvar_from_prop(sctx, GSASL_ANONYMOUS_TOKEN);
 
     cbrc = condition_check(ablock,
        US"server_condition (ANONYMOUS)", ablock->server_condition);
@@ -592,13 +601,8 @@ switch (prop)
     to the first release of Exim with this authenticator, they've been
     switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
 
-    propval = US gsasl_property_fast(sctx, GSASL_GSSAPI_DISPLAY_NAME);
-    auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
-    propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
-    auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US"";
-    expand_nmax = 2;
-    for (int i = 1; i <= 2; ++i)
-      expand_nlength[i] = Ustrlen(expand_nstring[i]);
+    set_exim_authvar_from_prop(sctx, GSASL_GSSAPI_DISPLAY_NAME);
+    set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
 
     /* In this one case, it perhaps makes sense to default back open?
     But for consistency, let's just mandate server_condition here too. */
@@ -612,64 +616,54 @@ switch (prop)
     HDEBUG(D_auth) debug_printf(" SCRAM_ITER\n");
     if (ob->server_scram_iter)
       {
+      set_exim_authvars_from_a_az_r_props(sctx);
       tmps = CS expand_string(ob->server_scram_iter);
+      HDEBUG(D_auth) debug_printf("  '%s'\n", tmps);
       gsasl_property_set(sctx, GSASL_SCRAM_ITER, tmps);
       cbrc = GSASL_OK;
       }
+    else
+      HDEBUG(D_auth) debug_printf("  option not set\n");
     break;
 
   case GSASL_SCRAM_SALT:
     HDEBUG(D_auth) debug_printf(" SCRAM_SALT\n");
-    if (ob->server_scram_iter)
+    if (ob->server_scram_salt)
       {
+      set_exim_authvars_from_a_az_r_props(sctx);
       tmps = CS expand_string(ob->server_scram_salt);
-      gsasl_property_set(sctx, GSASL_SCRAM_SALT, tmps);
+      HDEBUG(D_auth) debug_printf("  '%s'\n", tmps);
+      if (*tmps)
+       gsasl_property_set(sctx, GSASL_SCRAM_SALT, tmps);
       cbrc = GSASL_OK;
       }
+    else
+      HDEBUG(D_auth) debug_printf("  option not set\n");
     break;
 
   case GSASL_PASSWORD:
     HDEBUG(D_auth) debug_printf(" PASSWORD\n");
-    /* DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
+    /* SCRAM-SHA-1: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
+       DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
        CRAM-MD5: GSASL_AUTHID
        PLAIN: GSASL_AUTHID and GSASL_AUTHZID
        LOGIN: GSASL_AUTHID
      */
-    if (ob->server_scram_iter)
-      {
-      tmps = CS expand_string(ob->server_scram_iter);
-      gsasl_property_set(sctx, GSASL_SCRAM_ITER, tmps);
-      }
-    if (ob->server_scram_salt)
+    set_exim_authvars_from_a_az_r_props(sctx);
+
+    if (!ob->server_password)
       {
-      tmps = CS expand_string(ob->server_scram_salt);
-      gsasl_property_set(sctx, GSASL_SCRAM_SALT, tmps);
+      HDEBUG(D_auth) debug_printf("option not set\n");
+      break;
       }
-
-    /* Asking for GSASL_AUTHZID calls back into us if we use
-    gsasl_property_get(), thus the use of gsasl_property_fast().
-    Do we really want to hardcode limits per mechanism?  What happens when
-    a new mechanism is added to the library.  It *shouldn't* result in us
-    needing to add more glue, since avoiding that is a large part of the
-    point of SASL. */
-
-    propval = US gsasl_property_fast(sctx, GSASL_AUTHID);
-    auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
-    propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
-    auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US"";
-    propval = US gsasl_property_fast(sctx, GSASL_REALM);
-    auth_vars[2] = expand_nstring[3] = propval ? string_copy(propval) : US"";
-    expand_nmax = 3;
-    for (int i = 1; i <= 3; ++i)
-      expand_nlength[i] = Ustrlen(expand_nstring[i]);
-
     if (!(tmps = CS expand_string(ob->server_password)))
       {
-      sasl_error_should_defer = f.expand_string_forcedfail ? FALSE : TRUE;
+      sasl_error_should_defer = !f.expand_string_forcedfail;
       HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
          "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
       return GSASL_AUTHENTICATION_ERROR;
       }
+    HDEBUG(D_auth) debug_printf("  set\n");
     gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
 
     /* This is inadequate; don't think Exim's store stacks are geared
@@ -695,14 +689,12 @@ return cbrc;
 /******************************************************************************/
 
 #define PROP_OPTIONAL  BIT(0)
-#define PROP_STRINGPREP        BIT(1)
-
 
 static BOOL
 client_prop(Gsasl_session * sctx, Gsasl_property propnum, uschar * val,
   const uschar * why, unsigned flags, uschar * buffer, int buffsize)
 {
-uschar * s, * t;
+uschar * s;
 int rc;
 
 if (flags & PROP_OPTIONAL && !val) return TRUE;
@@ -711,26 +703,7 @@ if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
   string_format(buffer, buffsize, "%s", expand_string_message);
   return FALSE;
   }
-if (!*s) return TRUE;
-
-#ifdef SUPPORT_I18N
-if (flags & PROP_STRINGPREP)
-  {
-  if (gsasl_saslprep(CCS s, 0, CSS &t, &rc) != GSASL_OK)
-    {
-    string_format(buffer, buffsize, "Bad result from saslprep(%s): %s\n",
-                 why, stringprep_strerror(rc));
-    HDEBUG(D_auth) debug_printf("%s\n", buffer);
-    return FALSE;
-    }
-  gsasl_property_set(sctx, propnum, CS t);
-
-  free(t);
-  }
-else
-#endif
-  gsasl_property_set(sctx, propnum, CS s);
-
+if (*s) gsasl_property_set(sctx, propnum, CS s);
 return TRUE;
 }
 
@@ -753,8 +726,8 @@ auth_gsasl_options_block *ob =
 Gsasl_session * sctx = NULL;
 struct callback_exim_state cb_state;
 uschar * s;
-BOOL initial = TRUE, do_stringprep;
-int rc, yield = FAIL, flags;
+BOOL initial = TRUE;
+int rc, yield = FAIL;
 
 HDEBUG(D_auth)
   debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
@@ -797,14 +770,12 @@ gsasl_session_hook_set(sctx, &cb_state);
 
 /* Set properties */
 
-flags = Ustrncmp(ob->server_mech, "SCRAM-", 5) == 0 ? PROP_STRINGPREP : 0;
-
 if (  !client_prop(sctx, GSASL_PASSWORD, ob->client_password, US"password",
-                 flags, buffer, buffsize)
+                 0, buffer, buffsize)
    || !client_prop(sctx, GSASL_AUTHID, ob->client_username, US"username",
-                 flags, buffer, buffsize)
+                 0, buffer, buffsize)
    || !client_prop(sctx, GSASL_AUTHZID, ob->client_authz, US"authz",
-                 flags | PROP_OPTIONAL, buffer, buffsize)
+                 PROP_OPTIONAL, buffer, buffsize)
    )
   return ERROR;