Ejemplo n.º 1
0
Archivo: server.c Proyecto: cktan/tool2
static int
scram_start (Gsasl_session * sctx, void **mech_data, int plus)
{
  struct scram_server_state *state;
  char buf[MAX (SNONCE_ENTROPY_BYTES, DEFAULT_SALT_BYTES)];
  const char *p;
  int rc;

  state = (struct scram_server_state *) calloc (sizeof (*state), 1);
  if (state == NULL)
    return GSASL_MALLOC_ERROR;

  state->plus = plus;

  rc = gsasl_nonce (buf, SNONCE_ENTROPY_BYTES);
  if (rc != GSASL_OK)
    goto end;

  rc = gsasl_base64_to (buf, SNONCE_ENTROPY_BYTES, &state->snonce, NULL);
  if (rc != GSASL_OK)
    goto end;

  rc = gsasl_nonce (buf, DEFAULT_SALT_BYTES);
  if (rc != GSASL_OK)
    goto end;

  rc = gsasl_base64_to (buf, DEFAULT_SALT_BYTES, &state->sf.salt, NULL);
  if (rc != GSASL_OK)
    goto end;

  p = gsasl_property_get (sctx, GSASL_CB_TLS_UNIQUE);
  if (plus && !p)
    {
      rc = GSASL_NO_CB_TLS_UNIQUE;
      goto end;
    }
  if (p)
    {
      rc = gsasl_base64_from (p, strlen (p), &state->cbtlsunique,
			      &state->cbtlsuniquelen);
      if (rc != GSASL_OK)
	goto end;
    }

  *mech_data = state;

  return GSASL_OK;

end:
  free (state->sf.salt);
  free (state->snonce);
  free (state);
  return rc;
}
Ejemplo n.º 2
0
int
_gsasl_digest_md5_server_start (Gsasl_session * sctx, void **mech_data)
{
    _Gsasl_digest_md5_server_state *state;
    char nonce[NONCE_ENTROPY_BYTES];
    char *p;
    int rc;

    rc = gsasl_nonce (nonce, NONCE_ENTROPY_BYTES);
    if (rc != GSASL_OK)
        return rc;

    rc = gsasl_base64_to (nonce, NONCE_ENTROPY_BYTES, &p, NULL);
    if (rc != GSASL_OK)
        return rc;

    state = calloc (1, sizeof (*state));
    if (state == NULL)
    {
        free (p);
        return GSASL_MALLOC_ERROR;
    }

    state->challenge.qops = DIGEST_MD5_QOP_AUTH;
    state->challenge.ciphers = 0;

    state->challenge.nonce = p;
    state->challenge.utf8 = 1;

    *mech_data = state;

    return GSASL_OK;
}
Ejemplo n.º 3
0
/**
 * gsasl_step64:
 * @sctx: libgsasl client handle.
 * @b64input: input base64 encoded byte array.
 * @b64output: newly allocated output base64 encoded byte array.
 *
 * This is a simple wrapper around gsasl_step() that base64 decodes
 * the input and base64 encodes the output.
 *
 * The contents of the @b64output buffer is unspecified if this
 * functions returns anything other than %GSASL_OK or
 * %GSASL_NEEDS_MORE.  If this function return %GSASL_OK or
 * %GSASL_NEEDS_MORE, however, the @b64output buffer is allocated by
 * this function, and it is the responsibility of caller to deallocate
 * it by calling free (@b64output).
 *
 * Return value: Returns %GSASL_OK if authenticated terminated
 *   successfully, %GSASL_NEEDS_MORE if more data is needed, or error
 *   code.
 **/
int
gsasl_step64 (Gsasl_session * sctx, const char *b64input, char **b64output)
{
  size_t input_len = 0, output_len = 0;
  char *input = NULL, *output = NULL;
  int res;

  if (b64input)
    {
      res = gsasl_base64_from (b64input, strlen (b64input),
			       &input, &input_len);
      if (res != GSASL_OK)
	return GSASL_BASE64_ERROR;
    }

  res = gsasl_step (sctx, input, input_len, &output, &output_len);

  if (input != NULL)
    free (input);

  if (res == GSASL_OK || res == GSASL_NEEDS_MORE)
    {
      int tmpres = gsasl_base64_to (output, output_len, b64output, NULL);

      if (output != NULL)
	free (output);

      if (tmpres != GSASL_OK)
	return tmpres;
    }

  return res;
}
Ejemplo n.º 4
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;
}
Ejemplo n.º 5
0
Archivo: server.c Proyecto: cktan/tool2
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;
}
Ejemplo n.º 6
0
int
main (int argc, char *argv[])
{
  Gsasl *ctx = NULL;
  int res;
  char *in;
  char *connect_hostname = NULL;
  char *connect_service = NULL;
#ifdef HAVE_LIBGNUTLS
  gnutls_anon_client_credentials anoncred;
  gnutls_certificate_credentials x509cred;
#endif

  set_program_name (argv[0]);
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  /* This is necessary for modern MinGW compilers that provide working
     getaddrinfo function, which results in gnulib not detecting that
     it is broken.  The proper fix is for gnulib to wrap the
     getaddrinfo call and initialize Windows sockets in the
     wrapper.  */
  (void) gl_sockets_startup (SOCKETS_1_1);

  if (cmdline_parser (argc, argv, &args_info) != 0)
    return EXIT_FAILURE;

  if (args_info.version_given)
    {
      const char *p = PACKAGE_NAME;
      if (strcmp (gsasl_check_version (NULL), PACKAGE_VERSION) != 0)
	p = PACKAGE_STRING;
      version_etc (stdout, "gsasl", p, gsasl_check_version (NULL),
		   "Simon Josefsson", (char *) NULL);
      return EXIT_SUCCESS;
    }

  if (args_info.help_given)
    usage (EXIT_SUCCESS);

  if (!(args_info.client_flag || args_info.client_given) &&
      !args_info.server_given &&
      !args_info.client_mechanisms_flag && !args_info.server_mechanisms_flag)
    {
      error (0, 0, _("missing argument"));
      usage (EXIT_FAILURE);
    }

  if ((args_info.x509_cert_file_arg && !args_info.x509_key_file_arg) ||
      (!args_info.x509_cert_file_arg && args_info.x509_key_file_arg))
    error (EXIT_FAILURE, 0,
	   _("need both --x509-cert-file and --x509-key-file"));

  if (args_info.starttls_flag && args_info.no_starttls_flag)
    error (EXIT_FAILURE, 0,
	   _("cannot use both --starttls and --no-starttls"));

  if (args_info.smtp_flag && args_info.imap_flag)
    error (EXIT_FAILURE, 0, _("cannot use both --smtp and --imap"));

  if (!args_info.connect_given && args_info.inputs_num == 0 &&
      !args_info.client_given && !args_info.server_given &&
      !args_info.client_mechanisms_flag && !args_info.server_mechanisms_flag)
    {
      cmdline_parser_print_help ();
      emit_bug_reporting_address ();
      return EXIT_SUCCESS;
    }

  if (args_info.connect_given)
    {
      if (strrchr (args_info.connect_arg, ':'))
	{
	  connect_hostname = xstrdup (args_info.connect_arg);
	  *strrchr (connect_hostname, ':') = '\0';
	  connect_service =
	    xstrdup (strrchr (args_info.connect_arg, ':') + 1);
	}
      else
	{
	  connect_hostname = xstrdup (args_info.connect_arg);
	  if (args_info.smtp_flag)
	    connect_service = xstrdup ("smtp");
	  else
	    connect_service = xstrdup ("imap");
	}
    }
  else if (args_info.inputs_num > 0)
    {
      connect_hostname = args_info.inputs[0];
      if (args_info.inputs_num > 1)
	connect_service = args_info.inputs[1];
      else if (args_info.smtp_flag)
	connect_service = xstrdup ("smtp");
      else
	connect_service = xstrdup ("imap");
    }

  if (connect_service && !args_info.smtp_flag && !args_info.imap_flag)
    {
      if (strcmp (connect_service, "25") == 0 ||
	  strcmp (connect_service, "smtp") == 0)
	args_info.smtp_flag = 1;
      else
	args_info.imap_flag = 1;
    }

  if (args_info.imap_flag && !args_info.service_given)
    args_info.service_arg = xstrdup ("imap");

  if (args_info.smtp_flag && !args_info.service_given)
    args_info.service_arg = xstrdup ("smtp");

  if (args_info.imap_flag || args_info.smtp_flag)
    args_info.no_client_first_flag = 1;

  if (connect_hostname && !args_info.hostname_arg)
    args_info.hostname_arg = xstrdup (connect_hostname);

  if (!isatty (STDOUT_FILENO))
    setvbuf (stdout, NULL, _IOLBF, BUFSIZ);

  res = gsasl_init (&ctx);
  if (res != GSASL_OK)
    error (EXIT_FAILURE, 0, _("initialization failure: %s"),
	   gsasl_strerror (res));

  gsasl_callback_set (ctx, callback);

  if (args_info.client_mechanisms_flag || args_info.server_mechanisms_flag)
    {
      char *mechs;

      if (args_info.client_mechanisms_flag)
	res = gsasl_client_mechlist (ctx, &mechs);
      else
	res = gsasl_server_mechlist (ctx, &mechs);

      if (res != GSASL_OK)
	error (EXIT_FAILURE, 0, _("error listing mechanisms: %s"),
	       gsasl_strerror (res));

      if (!args_info.quiet_given)
	{
	  if (args_info.client_mechanisms_flag)
	    fprintf (stderr,
		     _("This client supports the following mechanisms:\n"));
	  else
	    fprintf (stderr,
		     _("This server supports the following mechanisms:\n"));
	}

      fprintf (stdout, "%s\n", mechs);

      free (mechs);

      return EXIT_SUCCESS;
    }

  if (args_info.connect_given || args_info.inputs_num > 0)
    {
      struct addrinfo hints;
      struct addrinfo *ai0, *ai;

      memset (&hints, 0, sizeof (hints));
      hints.ai_flags = AI_CANONNAME;
      hints.ai_socktype = SOCK_STREAM;
      res = getaddrinfo (connect_hostname, connect_service, &hints, &ai0);
      if (res != 0)
	error (EXIT_FAILURE, 0, "%s: %s", connect_hostname,
	       gai_strerror (res));

      for (ai = ai0; ai; ai = ai->ai_next)
	{
	  fprintf (stderr, "Trying %s...\n", quote (ai->ai_canonname ?
						    ai->ai_canonname :
						    connect_hostname));

	  sockfd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
	  if (sockfd < 0)
	    {
	      error (0, errno, "socket");
	      continue;
	    }

	  if (connect (sockfd, ai->ai_addr, ai->ai_addrlen) < 0)
	    {
	      int save_errno = errno;
	      close (sockfd);
	      sockfd = -1;
	      error (0, save_errno, "connect");
	      continue;
	    }
	  break;
	}

      if (sockfd < 0)
	error (EXIT_FAILURE, errno, "socket");

      freeaddrinfo (ai);
    }

  if (!greeting ())
    return 1;

#ifdef HAVE_LIBGNUTLS
  if (sockfd && !args_info.no_starttls_flag &&
      (args_info.starttls_flag || has_starttls ()))
    {
      res = gnutls_global_init ();
      if (res < 0)
	error (EXIT_FAILURE, 0, _("GnuTLS global initialization failed: %s"),
	       gnutls_strerror (res));

      res = gnutls_init (&session, GNUTLS_CLIENT);
      if (res < 0)
	error (EXIT_FAILURE, 0, _("GnuTLS initialization failed: %s"),
	       gnutls_strerror (res));

      res = gnutls_set_default_priority (session);
      if (res < 0)
	error (EXIT_FAILURE, 0, _("setting GnuTLS defaults failed: %s"),
	       gnutls_strerror (res));

      res = gnutls_anon_allocate_client_credentials (&anoncred);
      if (res < 0)
	error (EXIT_FAILURE, 0,
	       _("allocating anonymous GnuTLS credential: %s"),
	       gnutls_strerror (res));

      res = gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
      if (res < 0)
	error (EXIT_FAILURE, 0, _("setting anonymous GnuTLS credential: %s"),
	       gnutls_strerror (res));

      res = gnutls_certificate_allocate_credentials (&x509cred);
      if (res < 0)
	error (EXIT_FAILURE, 0, _("allocating X.509 GnuTLS credential: %s"),
	       gnutls_strerror (res));

      if (args_info.x509_cert_file_arg && args_info.x509_key_file_arg)
	res = gnutls_certificate_set_x509_key_file
	  (x509cred, args_info.x509_cert_file_arg,
	   args_info.x509_key_file_arg, GNUTLS_X509_FMT_PEM);
      if (res != GNUTLS_E_SUCCESS)
	error (EXIT_FAILURE, 0, _("loading X.509 GnuTLS credential: %s"),
	       gnutls_strerror (res));

      if (args_info.x509_ca_file_arg)
	{
	  res = gnutls_certificate_set_x509_trust_file
	    (x509cred, args_info.x509_ca_file_arg, GNUTLS_X509_FMT_PEM);
	  if (res < 0)
	    error (EXIT_FAILURE, 0, _("no X.509 CAs found: %s"),
		   gnutls_strerror (res));
	  if (res == 0)
	    error (EXIT_FAILURE, 0, _("no X.509 CAs found"));
	}

      res = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE,
				    x509cred);
      if (res < 0)
	error (EXIT_FAILURE, 0, _("setting X.509 GnuTLS credential: %s"),
	       gnutls_strerror (res));

      if (args_info.priority_arg)
	{
	  const char *err_pos;

	  res = gnutls_priority_set_direct (session, args_info.priority_arg,
					    &err_pos);
	  if (res < 0)
	    error (EXIT_FAILURE, 0,
		   _("setting GnuTLS cipher priority (%s): %s\n"),
		   gnutls_strerror (res), err_pos);
	}

      gnutls_transport_set_ptr (session, (gnutls_transport_ptr)
				(unsigned long) sockfd);

      if (!starttls ())
	return 1;

      res = gnutls_handshake (session);
      if (res < 0)
	error (EXIT_FAILURE, 0, _("GnuTLS handshake failed: %s"),
	       gnutls_strerror (res));

      if (args_info.x509_ca_file_arg)
	{
	  unsigned int status;

	  res = gnutls_certificate_verify_peers2 (session, &status);
	  if (res < 0)
	    error (EXIT_FAILURE, 0, _("verifying peer certificate: %s"),
		   gnutls_strerror (res));

	  if (status & GNUTLS_CERT_INVALID)
	    error (EXIT_FAILURE, 0, _("server certificate is not trusted"));

	  if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
	    error (EXIT_FAILURE, 0,
		   _("server certificate hasn't got a known issuer"));

	  if (status & GNUTLS_CERT_REVOKED)
	    error (EXIT_FAILURE, 0, _("server certificate has been revoked"));

	  if (status != 0)
	    error (EXIT_FAILURE, 0,
		   _("could not verify server certificate (rc=%d)"), status);
	}

#if HAVE_GNUTLS_SESSION_CHANNEL_BINDING
      if (!args_info.no_cb_flag)
	{
	  gnutls_datum cb;

	  res = gnutls_session_channel_binding (session,
						GNUTLS_CB_TLS_UNIQUE, &cb);
	  if (res != GNUTLS_E_SUCCESS)
	    error (EXIT_FAILURE, 0, _("getting channel binding failed: %s"),
		   gnutls_strerror (res));

	  res = gsasl_base64_to ((char *) cb.data, cb.size,
				 &b64cbtlsunique, NULL);
	  if (res != GSASL_OK)
	    error (EXIT_FAILURE, 0, "%s", gsasl_strerror (res));
	}
#endif

      using_tls = true;
    }
#endif

  if (args_info.client_flag || args_info.client_given
      || args_info.server_given)
    {
      char *out;
      char *b64output;
      size_t output_len;
      size_t b64output_len;
      const char *mech;
      Gsasl_session *xctx = NULL;

      if (!select_mechanism (&in))
	return 1;

      mech = gsasl_client_suggest_mechanism (ctx, in);
      if (mech == NULL)
	{
	  fprintf (stderr, _("Cannot find mechanism...\n"));
	  return 0;
	}

      if (args_info.mechanism_arg)
	mech = args_info.mechanism_arg;

      if (!authenticate (mech))
	return 1;

      /* Authenticate using mechanism */

      if (args_info.server_flag)
	res = gsasl_server_start (ctx, mech, &xctx);
      else
	res = gsasl_client_start (ctx, mech, &xctx);
      if (res != GSASL_OK)
	error (EXIT_FAILURE, 0, _("mechanism unavailable: %s"),
	       gsasl_strerror (res));

      in = NULL;
      out = NULL;

      if (!args_info.server_flag && args_info.no_client_first_flag)
	{
	  res = GSASL_NEEDS_MORE;
	  goto no_client_first;
	}

      do
	{
	  int res2;

	  res = gsasl_step64 (xctx, in, &out);
	  if (res != GSASL_NEEDS_MORE && res != GSASL_OK)
	    break;

	  if (!step_send (out))
	    return 1;

	no_client_first:
	  if (!args_info.quiet_given &&
	      !args_info.imap_flag && !args_info.smtp_flag)
	    {
	      if (args_info.server_flag)
		fprintf (stderr, _("Enter base64 authentication data "
				   "from client (press RET if none):\n"));
	      else
		fprintf (stderr, _("Enter base64 authentication data "
				   "from server (press RET if none):\n"));
	    }

	  /* Return 1 on token, 2 on protocol success, 3 on protocol fail, 0 on
	     errors. */
	  res2 = step_recv (&in);
	  if (!res2)
	    return 1;
	  if (res2 == 3)
	    error (EXIT_FAILURE, 0, _("server error"));
	  if (res2 == 2)
	    break;
	}
      while (args_info.imap_flag || args_info.smtp_flag
	     || res == GSASL_NEEDS_MORE);

      if (res != GSASL_OK)
	error (EXIT_FAILURE, 0, _("mechanism error: %s"),
	       gsasl_strerror (res));

      if (!args_info.quiet_given)
	{
	  if (args_info.server_flag)
	    fprintf (stderr, _("Server authentication "
			       "finished (client trusted)...\n"));
	  else
	    fprintf (stderr, _("Client authentication "
			       "finished (server trusted)...\n"));
	  fflush (stderr);
	}

      /* Transfer application payload */
      if (args_info.application_data_flag)
	{
	  struct pollfd pfd[2];
	  char *sockbuf = NULL;
	  /* we read chunks of 1000 bytes at a time */
	  size_t sockpos = 0, sockalloc = 0, sockalloc1 = 1000;

	  /* Setup pollfd structs... */
	  pfd[0].fd = STDIN_FILENO;
	  pfd[0].events = POLLIN;
	  if (sockfd)
	    {
	      pfd[1].fd = sockfd;
	      pfd[1].events = POLLIN;
	    }

	  if (!args_info.quiet_given)
	    {
	      fprintf (stderr,
		       _("Enter application data (EOF to finish):\n"));
	      fflush (stderr);
	    }

	  while (1)
	    {
	      int rc;

	      pfd[0].revents = 0;
	      pfd[1].revents = 0;

	      rc = poll (pfd, sockfd ? 2 : 1, -1);
	      if (rc < 0 && errno == EINTR)
		continue;

	      /* Always check for errors */
	      if (rc < 0)
		error (EXIT_FAILURE, errno, "poll");

	      /* We got data to read from stdin.. */
	      if ((pfd[0].revents & (POLLIN | POLLERR)) == POLLIN)
		{
		  char *line = NULL;
		  size_t n;
		  ssize_t len;

		  len = getline (&line, &n, stdin);
		  if (len <= 0)
		    break;

		  if (args_info.imap_flag || args_info.smtp_flag)
		    {
		      if (len < 2 || strcmp (&line[len - 2], "\r\n") != 0)
			{
			  line = xrealloc (line, len + 2);
			  line[len - 1] = '\r';
			  line[len] = '\n';
			  line[len + 1] = '\0';
			  len++;
			}
		    }
		  else
		    {
		      len--;
		      line[len] = '\0';
		    }

		  res = gsasl_encode (xctx, line, len, &out, &output_len);
		  if (res != GSASL_OK)
		    break;

		  if (sockfd)
		    {
#ifdef HAVE_LIBGNUTLS
		      if (using_tls)
			len = gnutls_record_send (session, out, output_len);
		      else
#endif
			len = write (sockfd, out, output_len);
		      if (len != output_len)
			error (EXIT_FAILURE, errno, "write");
		    }
		  else if (!(strlen (line) == output_len &&
			     memcmp (line, out, output_len) == 0))
		    {
		      res = gsasl_base64_to (out, output_len,
					     &b64output, &b64output_len);
		      if (res != GSASL_OK)
			break;

		      if (!args_info.quiet_given)
			fprintf (stderr, _("Base64 encoded application "
					   "data to send:\n"));
		      fprintf (stdout, "%s\n", b64output);

		      free (b64output);
		    }

		  free (line);
		  free (out);
		}
	      /* If there was an error, quit.  */
	      else if (pfd[0].revents & (POLLERR | POLLHUP))
		{
		  error (0, 0, "poll stdin");
		  break;
		}

	      /* We got data to read from the socket.. */
	      if (sockfd && (pfd[1].revents & (POLLIN | POLLERR)) == POLLIN)
		{
		  ssize_t len;

		  if (sockalloc == sockpos)
		    sockbuf = x2realloc (sockbuf, &sockalloc1);
		  sockalloc = sockalloc1;

#ifdef HAVE_LIBGNUTLS
		  if (using_tls)
		    len = gnutls_record_recv (session, &sockbuf[sockpos],
					      sockalloc - sockpos);
		  else
#endif
		    len = recv (sockfd, &sockbuf[sockpos],
				sockalloc - sockpos, 0);
		  if (len <= 0)
		    break;

		  sockpos += len;

		  res = gsasl_decode (xctx, sockbuf, sockpos,
				      &out, &output_len);
		  if (res == GSASL_NEEDS_MORE)
		    {
#define MAX_INPUT_SIZE	0x100000
		      if (sockpos > MAX_INPUT_SIZE)
			error (EXIT_FAILURE, 0,
			       _("SASL record too large: %zu\n"), sockpos);
		      continue;
		    }
		  if (res != GSASL_OK)
		    break;

		  free (sockbuf);
		  sockbuf = NULL;
		  sockpos = 0;
		  sockalloc = 0;
		  sockalloc1 = 1000;

		  printf ("%.*s", (int) output_len, out);
		  free (out);
		}
	      /* If there was an error, quit.  */
	      else if (pfd[1].revents & (POLLERR | POLLHUP))
		{
		  error (0, 0, "poll socket");
		  break;
		}
	    }

	  if (res != GSASL_OK)
	    error (EXIT_FAILURE, 0, _("encoding error: %s"),
		   gsasl_strerror (res));
	}

      if (!args_info.quiet_given)
	fprintf (stderr, _("Session finished...\n"));

      if (!logout ())
	return 1;

      gsasl_finish (xctx);
    }

  if (sockfd)
    {
#ifdef HAVE_LIBGNUTLS
      if (using_tls)
	{
	  res = gnutls_bye (session, GNUTLS_SHUT_RDWR);
	  if (res < 0)
	    error (EXIT_FAILURE, 0,
		   _("terminating GnuTLS session failed: %s"),
		   gnutls_strerror (res));

	}
#endif
      shutdown (sockfd, SHUT_RDWR);
      close (sockfd);
    }

  gsasl_done (ctx);

#ifdef HAVE_LIBGNUTLS
  if (using_tls)
    {
      gnutls_deinit (session);
      gnutls_anon_free_client_credentials (anoncred);
      gnutls_certificate_free_credentials (x509cred);
      gnutls_global_deinit ();
    }
#endif

  return 0;
}