Exemple #1
0
static std::string Base64Encode(const char * input, size_t len) {
    int rc = 0;
    size_t outLen;
    char * output = NULL;
    std::string retval;

    if (GSASL_OK != (rc = gsasl_base64_to(input, len, &output, &outLen))) {
        assert(GSASL_MALLOC_ERROR == rc);
        throw std::bad_alloc();
    }

    assert(NULL != output);
    retval = output;
    gsasl_free(output);

    for (size_t i = 0 ; i < retval.length(); ++i) {
        switch (retval[i]) {
        case '+':
            retval[i] = '-';
            break;

        case '/':
            retval[i] = '_';
            break;

        case '=':
            retval.resize(i);
            break;

        default:
            break;
        }
    }

    return retval;
}
Exemple #2
0
int
_gsasl_scram_sha1_server_step (Gsasl_session * sctx,
			       void *mech_data,
			       const char *input,
			       size_t input_len,
			       char **output, size_t * output_len)
{
  struct scram_server_state *state = mech_data;
  int res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES;
  int rc;

  *output = NULL;
  *output_len = 0;

  switch (state->step)
    {
    case 0:
      {
	if (input_len == 0)
	  return GSASL_NEEDS_MORE;

	if (scram_parse_client_first (input, input_len, &state->cf) < 0)
	  return GSASL_MECHANISM_PARSE_ERROR;

	/* In PLUS server mode, we require use of channel bindings. */
	if (state->plus && state->cf.cbflag != 'p')
	  return GSASL_AUTHENTICATION_ERROR;

	/* In non-PLUS mode, but where have channel bindings data (and
	   thus advertised PLUS) we reject a client 'y' cbflag. */
	if (!state->plus
	    && state->cbtlsuniquelen > 0 && state->cf.cbflag == 'y')
	  return GSASL_AUTHENTICATION_ERROR;

	/* Check that username doesn't fail SASLprep. */
	{
	  char *tmp;
	  rc = gsasl_saslprep (state->cf.username, GSASL_ALLOW_UNASSIGNED,
			       &tmp, NULL);
	  if (rc != GSASL_OK || *tmp == '\0')
	    return GSASL_AUTHENTICATION_ERROR;
	  gsasl_free (tmp);
	}

	{
	  const char *p;

	  /* Save "gs2-header" and "message-bare" for next step. */
	  p = memchr (input, ',', input_len);
	  if (!p)
	    return GSASL_AUTHENTICATION_ERROR;
	  p++;
	  p = memchr (p, ',', input_len - (p - input));
	  if (!p)
	    return GSASL_AUTHENTICATION_ERROR;
	  p++;

	  state->gs2header = malloc (p - input + 1);
	  if (!state->gs2header)
	    return GSASL_MALLOC_ERROR;
	  memcpy (state->gs2header, input, p - input);
	  state->gs2header[p - input] = '\0';

	  state->cfmb_str = malloc (input_len - (p - input) + 1);
	  if (!state->cfmb_str)
	    return GSASL_MALLOC_ERROR;
	  memcpy (state->cfmb_str, p, input_len - (p - input));
	  state->cfmb_str[input_len - (p - input)] = '\0';
	}

	/* Create new nonce. */
	{
	  size_t cnlen = strlen (state->cf.client_nonce);

	  state->sf.nonce = malloc (cnlen + SNONCE_ENTROPY_BYTES + 1);
	  if (!state->sf.nonce)
	    return GSASL_MALLOC_ERROR;

	  memcpy (state->sf.nonce, state->cf.client_nonce, cnlen);
	  memcpy (state->sf.nonce + cnlen, state->snonce,
		  SNONCE_ENTROPY_BYTES);
	  state->sf.nonce[cnlen + SNONCE_ENTROPY_BYTES] = '\0';
	}

	gsasl_property_set (sctx, GSASL_AUTHID, state->cf.username);
	gsasl_property_set (sctx, GSASL_AUTHZID, state->cf.authzid);

	{
	  const char *p = gsasl_property_get (sctx, GSASL_SCRAM_ITER);
	  if (p)
	    state->sf.iter = strtoul (p, NULL, 10);
	  if (!p || state->sf.iter == 0 || state->sf.iter == ULONG_MAX)
	    state->sf.iter = 4096;
	}

	{
	  const char *p = gsasl_property_get (sctx, GSASL_SCRAM_SALT);
	  if (p)
	    {
	      free (state->sf.salt);
	      state->sf.salt = strdup (p);
	    }
	}

	rc = scram_print_server_first (&state->sf, &state->sf_str);
	if (rc != 0)
	  return GSASL_MALLOC_ERROR;

	*output = strdup (state->sf_str);
	if (!*output)
	  return GSASL_MALLOC_ERROR;
	*output_len = strlen (*output);

	state->step++;
	return GSASL_NEEDS_MORE;
	break;
      }

    case 1:
      {
	if (scram_parse_client_final (input, input_len, &state->cl) < 0)
	  return GSASL_MECHANISM_PARSE_ERROR;

	if (strcmp (state->cl.nonce, state->sf.nonce) != 0)
	  return GSASL_AUTHENTICATION_ERROR;

	/* Base64 decode the c= field and check that it matches
	   client-first.  Also check channel binding data. */
	{
	  size_t len;

	  rc = gsasl_base64_from (state->cl.cbind, strlen (state->cl.cbind),
				  &state->cbind, &len);
	  if (rc != 0)
	    return rc;

	  if (state->cf.cbflag == 'p')
	    {
	      if (len < strlen (state->gs2header))
		return GSASL_AUTHENTICATION_ERROR;

	      if (memcmp (state->cbind, state->gs2header,
			  strlen (state->gs2header)) != 0)
		return GSASL_AUTHENTICATION_ERROR;

	      if (len - strlen (state->gs2header) != state->cbtlsuniquelen)
		return GSASL_AUTHENTICATION_ERROR;

	      if (memcmp (state->cbind + strlen (state->gs2header),
			  state->cbtlsunique, state->cbtlsuniquelen) != 0)
		return GSASL_AUTHENTICATION_ERROR;
	    }
	  else
	    {
	      if (len != strlen (state->gs2header))
		return GSASL_AUTHENTICATION_ERROR;

	      if (memcmp (state->cbind, state->gs2header, len) != 0)
		return GSASL_AUTHENTICATION_ERROR;
	    }
	}

	/* Base64 decode client proof and check that length matches
	   SHA-1 size. */
	{
	  size_t len;

	  rc = gsasl_base64_from (state->cl.proof, strlen (state->cl.proof),
				  &state->clientproof, &len);
	  if (rc != 0)
	    return rc;
	  if (len != 20)
	    return GSASL_MECHANISM_PARSE_ERROR;
	}

	{
	  const char *p;

	  /* Get StoredKey and ServerKey */
	  if ((p = gsasl_property_get (sctx, GSASL_PASSWORD)))
	    {
	      Gc_rc err;
	      char *salt;
	      size_t saltlen;
	      char saltedpassword[20];
	      char *clientkey;
	      char *preppasswd;

	      rc = gsasl_saslprep (p, 0, &preppasswd, NULL);
	      if (rc != GSASL_OK)
		return rc;

	      rc = gsasl_base64_from (state->sf.salt, strlen (state->sf.salt),
				      &salt, &saltlen);
	      if (rc != 0)
		{
		  gsasl_free (preppasswd);
		  return rc;
		}

	      /* SaltedPassword := Hi(password, salt) */
	      err = gc_pbkdf2_sha1 (preppasswd, strlen (preppasswd),
				    salt, saltlen,
				    state->sf.iter, saltedpassword, 20);
	      gsasl_free (preppasswd);
	      gsasl_free (salt);
	      if (err != GC_OK)
		return GSASL_MALLOC_ERROR;

	      /* ClientKey := HMAC(SaltedPassword, "Client Key") */
#define CLIENT_KEY "Client Key"
	      rc = gsasl_hmac_sha1 (saltedpassword, 20,
				    CLIENT_KEY, strlen (CLIENT_KEY),
				    &clientkey);
	      if (rc != 0)
		return rc;

	      /* StoredKey := H(ClientKey) */
	      rc = gsasl_sha1 (clientkey, 20, &state->storedkey);
	      free (clientkey);
	      if (rc != 0)
		return rc;

	      /* ServerKey := HMAC(SaltedPassword, "Server Key") */
#define SERVER_KEY "Server Key"
	      rc = gsasl_hmac_sha1 (saltedpassword, 20,
				    SERVER_KEY, strlen (SERVER_KEY),
				    &state->serverkey);
	      if (rc != 0)
		return rc;
	    }
	  else
	    return GSASL_NO_PASSWORD;

	  /* Compute AuthMessage */
	  {
	    size_t len;
	    int n;

	    /* Get client-final-message-without-proof. */
	    p = memmem (input, input_len, ",p=", 3);
	    if (!p)
	      return GSASL_MECHANISM_PARSE_ERROR;
	    len = p - input;

	    n = asprintf (&state->authmessage, "%s,%.*s,%.*s",
			  state->cfmb_str,
			  (int) strlen (state->sf_str), state->sf_str,
			  (int) len, input);
	    if (n <= 0 || !state->authmessage)
	      return GSASL_MALLOC_ERROR;
	  }

	  /* Check client proof. */
	  {
	    char *clientsignature;
	    char *maybe_storedkey;

	    /* ClientSignature := HMAC(StoredKey, AuthMessage) */
	    rc = gsasl_hmac_sha1 (state->storedkey, 20,
				  state->authmessage,
				  strlen (state->authmessage),
				  &clientsignature);
	    if (rc != 0)
	      return rc;

	    /* ClientKey := ClientProof XOR ClientSignature */
	    memxor (clientsignature, state->clientproof, 20);

	    rc = gsasl_sha1 (clientsignature, 20, &maybe_storedkey);
	    free (clientsignature);
	    if (rc != 0)
	      return rc;

	    rc = memcmp (state->storedkey, maybe_storedkey, 20);
	    free (maybe_storedkey);
	    if (rc != 0)
	      return GSASL_AUTHENTICATION_ERROR;
	  }

	  /* Generate server verifier. */
	  {
	    char *serversignature;

	    /* ServerSignature := HMAC(ServerKey, AuthMessage) */
	    rc = gsasl_hmac_sha1 (state->serverkey, 20,
				  state->authmessage,
				  strlen (state->authmessage),
				  &serversignature);
	    if (rc != 0)
	      return rc;

	    rc = gsasl_base64_to (serversignature, 20,
				  &state->sl.verifier, NULL);
	    free (serversignature);
	    if (rc != 0)
	      return rc;
	  }
	}

	rc = scram_print_server_final (&state->sl, output);
	if (rc != 0)
	  return GSASL_MALLOC_ERROR;
	*output_len = strlen (*output);

	state->step++;
	return GSASL_OK;
	break;
      }

    default:
      break;
    }

  return res;
}
Exemple #3
0
void
doit (void)
{
  Gsasl *ctx = NULL;
  Gsasl_session *server = NULL, *client = NULL;
  char *s1 = NULL, *s2 = NULL;
  int rc, res1, res2;

  if (getenv ("GNUGSS") && strcmp (getenv ("GNUGSS"), "no") == 0)
    {
      fail ("Not using GNU GSS, skipping self-test.\n");
      exit (77);
    }

  rc = gsasl_init (&ctx);
  if (rc != GSASL_OK)
    {
      fail ("gsasl_init() failed (%d):\n%s\n", rc, gsasl_strerror (rc));
      return;
    }

  if (!gsasl_client_support_p (ctx, "GS2-KRB5")
      || !gsasl_server_support_p (ctx, "GS2-KRB5"))
    {
      gsasl_done (ctx);
      fail ("No support for GS2-KRB5.\n");
      exit (77);
    }

  gsasl_callback_set (ctx, callback);

  for (i = 0; i < 5; i++)
    {
      bool client_first = (i % 2) == 0;

      rc = gsasl_server_start (ctx, "GS2-KRB5", &server);
      if (rc != GSASL_OK)
	{
	  fail ("gsasl_server_start() failed (%d):\n%s\n",
		rc, gsasl_strerror (rc));
	  return;
	}
      rc = gsasl_client_start (ctx, "GS2-KRB5", &client);
      if (rc != GSASL_OK)
	{
	  fail ("gsasl_client_start() failed (%d):\n%s\n",
		rc, gsasl_strerror (rc));
	  return;
	}

      if (client_first)
	{
	  rc = gsasl_step64 (client, NULL, &s1);
	  if (rc != GSASL_OK && rc != GSASL_NEEDS_MORE)
	    {
	      fail ("gsasl_step64 failed (%d):\n%s\n", rc,
		    gsasl_strerror (rc));
	      return;
	    }

	  if (debug)
	    printf ("C: %s [%c]\n", s1, ret_char (rc));
	}

      do
	{
	  res1 = gsasl_step64 (server, s1, &s2);
	  if (s1 == NULL && res1 == GSASL_OK)
	    fail ("gsasl_step64 direct success?\n");
	  if (s1)
	    {
	      gsasl_free (s1);
	      s1 = NULL;
	    }
	  if (res1 != GSASL_OK && res1 != GSASL_NEEDS_MORE)
	    {
	      fail ("gsasl_step64 (1) failed (%d):\n%s\n", res1,
		    gsasl_strerror (res1));
	      return;
	    }

	  if (debug)
	    printf ("S: %s [%c]\n", s2, ret_char (res1));

	  res2 = gsasl_step64 (client, s2, &s1);
	  gsasl_free (s2);
	  if (res2 != GSASL_OK && res2 != GSASL_NEEDS_MORE)
	    {
	      fail ("gsasl_step64 (2) failed (%d):\n%s\n", res2,
		    gsasl_strerror (res2));
	      return;
	    }

	  if (debug)
	    printf ("C: %s [%c]\n", s1, ret_char (res2));
	}
      while (res1 != GSASL_OK || res2 != GSASL_OK);

      if (s1)
	{
	  gsasl_free (s1);
	  s1 = NULL;
	}

      if (debug)
	printf ("\n");

      gsasl_finish (client);
      gsasl_finish (server);
    }

  gsasl_done (ctx);
}
static void sasl_authenticate( SV *client, mongo_link *link ) { 
  Gsasl *ctx = NULL;
  Gsasl_session *session;
  SV *username, *mechanism, *conv_id;
  HV *result;       /* response document from mongod */
  char *p, *buf;    /* I/O buffers for gsasl */
  int rc;
  char out_buf[8192];

  mechanism = perl_mongo_call_method( client, "sasl_mechanism", 0, 0 );
  if ( !SvOK( mechanism ) ) { 
    croak( "MongoDB: Could not retrieve SASL mechanism from client object\n" );
  }

  if ( strncmp( "PLAIN", SvPV_nolen( mechanism ), 5 ) == 0 ) { 
    /* SASL PLAIN does not require a libgsasl conversation loop, so we can handle it elsewhere */
    return perl_mongo_call_method( client, "_sasl_plain_authenticate", 0, 0 );
  }

  if ( ( rc = gsasl_init( &ctx ) ) != GSASL_OK ) { 
    croak( "MongoDB: Cannot initialize libgsasl (%d): %s\n", rc, gsasl_strerror(rc) );  
  }

  if ( ( rc = gsasl_client_start( ctx, SvPV_nolen( mechanism ), &session ) ) != GSASL_OK ) { 
    croak( "MongoDB: Cannot initialize SASL client (%d): %s\n", rc, gsasl_strerror(rc) );
  }

  username = perl_mongo_call_method( client, "username", 0, 0 );
  if ( !SvOK( username ) ) { 
    croak( "MongoDB: Cannot start SASL session without username. Specify username in constructor\n" );
  }
 
  gsasl_property_set( session, GSASL_SERVICE,  "mongodb" );
  gsasl_property_set( session, GSASL_HOSTNAME, link->master->host );
  gsasl_property_set( session, GSASL_AUTHID,   SvPV_nolen( username ) ); 

  rc = gsasl_step64( session, "", &p );
  if ( ( rc != GSASL_OK ) && ( rc != GSASL_NEEDS_MORE ) ) { 
    croak( "MongoDB: No data from GSSAPI. Did you run kinit?\n" );
  }

  if ( ! strncpy( out_buf, p, 8192 ) ) {
    croak( "MongoDB: Unable to copy SASL output buffer\n" );
  }
  gsasl_free( p );

  result = (HV *)SvRV( perl_mongo_call_method( client, "_sasl_start", 0, 2, newSVpv( out_buf, 0 ), mechanism ) );

#if 0  
  fprintf( stderr, "result conv id = [%s]\n", SvPV_nolen( *hv_fetch( result, "conversationId", 14, FALSE ) ) );
  fprintf( stderr, "result payload = [%s]\n", SvPV_nolen( *hv_fetch( result, "payload",         7, FALSE ) ) );
#endif

  buf = SvPV_nolen( *hv_fetch( result, "payload", 7, FALSE ) );
  conv_id = *hv_fetch( result, "conversationId", 14, FALSE ); 
 
  do { 
    rc = gsasl_step64( session, buf, &p );
    if ( ( rc != GSASL_OK ) && ( rc != GSASL_NEEDS_MORE ) ) {
      croak( "MongoDB: SASL step error (%d): %s\n", rc, gsasl_strerror(rc) );
    }

    if ( ! strncpy( out_buf, p, 8192 ) ) { 
      croak( "MongoDB: Unable to copy SASL output buffer\n" );
    }
    gsasl_free( p );

    result = (HV *)SvRV( perl_mongo_call_method( client, "_sasl_continue", 0, 2, newSVpv( out_buf, 0 ), conv_id ) );
#if 0 
    fprintf( stderr, "result conv id = [%s]\n", SvPV_nolen( *hv_fetch( result, "conversationId", 14, FALSE ) ) );
    fprintf( stderr, "result payload = [%s]\n", SvPV_nolen( *hv_fetch( result, "payload",         7, FALSE ) ) );
#endif

    buf = SvPV_nolen( *hv_fetch( result, "payload", 7, FALSE ) );

  } while( rc == GSASL_NEEDS_MORE );

  if ( rc != GSASL_OK ) { 
    croak( "MongoDB: SASL Authentication error (%d): %s\n", rc, gsasl_strerror(rc) );
  }

  gsasl_finish( session );
  gsasl_done( ctx );
}
Exemple #5
0
static void
client_xmpp (Gsasl_session * session)
{
  char buf[BUFSIZ] = "";
  char *p;
  int rc;

  /* This loop mimics a protocol where the client send data first,
     which is something that XMPP supports.  For simplicity, it
     requires that server send the XML blob on one line and XML parser
     is not complete.  */

  /* Generate client output. */
  rc = gsasl_step64 (session, buf, &p);
  if (rc != GSASL_NEEDS_MORE)
    {
      printf ("SAML20 step error (%d): %s\n", rc, gsasl_strerror (rc));
      return;
    }

  printf ("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
	  "mechanism='SAML20'>%s</auth>\n", p);

  do
    {
      char *b64;

      p = fgets (buf, sizeof (buf) - 1, stdin);
      if (p == NULL)
	{
	  perror ("fgets");
	  return;
	}

      if (buf[strlen (buf) - 1] == '\n')
        buf[strlen (buf) - 1] = '\0';

      b64 = xmltob64 (buf);

      printf ("parsed: '%s'\n", b64);

      rc = gsasl_step64 (session, b64, &p);
      if (rc != GSASL_NEEDS_MORE && rc != GSASL_OK)
	{
	  printf ("SAML20 step error (%d): %s\n", rc, gsasl_strerror (rc));
	  return;
	}

      printf ("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
	      "%s</response>\n", p);

      gsasl_free (p);
    }
  while (rc == GSASL_NEEDS_MORE);

  if (rc != GSASL_OK)
    {
      printf ("Authentication error (%d): %s\n", rc, gsasl_strerror (rc));
      return;
    }

  /* The client is done.  Here you would typically check if the server
     let the client in.  If not, you could try again. */

  printf ("If server accepted us, we're done.\n");
}
Exemple #6
0
void
doit (void)
{
  Gsasl *ctx = NULL;
  Gsasl_session *sctx = NULL;
  char *out = NULL;
  int i, j;
  int res;

  if (!gsasl_check_version (GSASL_VERSION))
    fail ("gsasl_check_version failure");

  success ("Header version %s library version %s\n",
	   GSASL_VERSION, gsasl_check_version (NULL));

  res = gsasl_init (&ctx);
  if (res != GSASL_OK)
    {
      fail ("gsasl_init() failed (%d):\n%s\n", res, gsasl_strerror (res));
      return;
    }

  gsasl_callback_set (ctx, cb);

  res = gsasl_client_mechlist (ctx, &out);
  if (res != GSASL_OK)
    fail ("gsasl_client_mechlist() failed (%d):\n%s\n",
	  res, gsasl_strerror (res));
  success ("client_mechlist: %s\n", out);
  gsasl_free (out);
  out = NULL;

  res = gsasl_server_mechlist (ctx, &out);
  if (res != GSASL_OK)
    fail ("gsasl_server_mechlist() failed (%d):\n%s\n",
	  res, gsasl_strerror (res));
  success ("server_mechlist: %s\n", out);
  gsasl_free (out);
  out = NULL;

  for (i = 0; i < sizeof (sasltv) / sizeof (sasltv[0]); i++)
    {
      gsasl_callback_hook_set (ctx, &i);

      if (debug)
	printf ("Entry %d %s mechanism %s:\n",
		i, sasltv[i].clientp ? "client" : "server", sasltv[i].mech);

      if (sasltv[i].clientp)
	res = gsasl_client_support_p (ctx, sasltv[i].mech);
      else
	res = gsasl_server_support_p (ctx, sasltv[i].mech);
      if (!res)
	continue;

      if (sasltv[i].clientp)
	res = gsasl_client_start (ctx, sasltv[i].mech, &sctx);
      else
	res = gsasl_server_start (ctx, sasltv[i].mech, &sctx);
      if (res != GSASL_OK)
	{
	  fail ("SASL %s start for mechanism %s failed (%d):\n%s\n",
		sasltv[i].clientp ? "client" : "server",
		sasltv[i].mech, res, gsasl_strerror (res));
	  continue;
	}

      for (j = 0; sasltv[i].step[j]; j += 2)
	{
	  gsasl_session_hook_set (sctx, &j);

	  if (debug)
	    printf ("Input : %s\n",
		    sasltv[i].step[j] ? sasltv[i].step[j] : "");

	  res = gsasl_step64 (sctx, sasltv[i].step[j], &out);

	  if (debug)
	    printf ("Output: %s\n", out ? out : "(null)");

	  if (res != GSASL_OK && res != GSASL_NEEDS_MORE)
	    {
	      fail ("gsasl_step64 failed (%d): %s", res,
		    gsasl_strerror (res));
	      break;
	    }

	  if (strlen (out) !=
	      strlen (sasltv[i].step[j + 1] ? sasltv[i].step[j + 1] : ""))
	    {
	      printf ("Expected: %s\n", sasltv[i].step[j + 1] ?
		      sasltv[i].step[j + 1] : "");
	      fail
		("SASL entry %d mechanism %s client step %d length error\n",
		 i, sasltv[i].mech, j);
	      j = -1;
	      break;
	    }

	  if (strcmp (out, sasltv[i].step[j + 1] ?
		      sasltv[i].step[j + 1] : "") != 0)
	    {
	      printf ("Expected: %s\n", sasltv[i].step[j + 1] ?
		      sasltv[i].step[j + 1] : "");
	      fail ("SASL entry %d mechanism %s client step %d data error\n",
		    i, sasltv[i].mech, j);
	      j = -1;
	      break;
	    }

	  gsasl_free (out);
	  out = NULL;

	  if (strcmp (sasltv[i].mech, "SECURID") != 0 && res == GSASL_OK)
	    break;
	}

      if (j != (size_t) - 1 && res == GSASL_OK && sasltv[i].step[j + 2])
	fail ("SASL entry %d mechanism %s step %d code ended prematurely\n",
	      i, sasltv[i].mech, j);
      else if (j != (size_t) - 1 && res == GSASL_NEEDS_MORE)
	fail ("SASL entry %d mechanism %s step %d table ended prematurely\n",
	      i, sasltv[i].mech, j);
      else if (j != (size_t) - 1 && res != GSASL_OK)
	fail ("SASL entry %d mechanism %s step %d failed (%d):\n%s\n",
	      i, sasltv[i].mech, j, res, gsasl_strerror (res));
      else
	printf ("PASS: simple %s %s %d\n", sasltv[i].mech,
		sasltv[i].clientp ? "client" : "server", i);

      {
	size_t outlen;

	res = gsasl_encode (sctx, "foo", 3, &out, &outlen);
	if (res != GSASL_OK)
	  fail ("gsasl_encode %d: %s\n", res, gsasl_strerror (res));
	if (outlen != 3 && memcmp (out, "foo", outlen) != 0)
	  fail ("gsasl_encode memcmp: %.*s\n", (int) outlen, out);
	gsasl_free (out);
	out = NULL;

	res = gsasl_decode (sctx, "foo", 3, &out, &outlen);
	if (res != GSASL_OK)
	  fail ("gsasl_decode %d: %s\n", res, gsasl_strerror (res));
	if (outlen != 3 && memcmp (out, "foo", outlen) != 0)
	  fail ("gsasl_decode memcmp: %.*s\n", (int) outlen, out);
	gsasl_free (out);
	out = NULL;
      }

      gsasl_finish (sctx);

      if (debug)
	printf ("\n");
    }

  gsasl_done (ctx);

  /* Sanity check interfaces. */
  gsasl_finish (NULL);
  gsasl_done (NULL);
}