Exemplo n.º 1
0
	bool setsecprops()
	{
		sasl_security_properties_t secprops;
		secprops.min_ssf = ssf_min;
		secprops.max_ssf = ssf_max;
		secprops.maxbufsize = SASL_BUFSIZE;
		secprops.property_names = NULL;
		secprops.property_values = NULL;
		secprops.security_flags = secflags;
		int r = sasl_setprop(con, SASL_SEC_PROPS, &secprops);
		if(r != SASL_OK)
			return false;

		if(!ext_authid.isEmpty()) {
			QByteArray cs = ext_authid.toLatin1();
			const char *authid = cs.data();
			sasl_ssf_t ssf = ext_ssf;
			r = sasl_setprop(con, SASL_SSF_EXTERNAL, &ssf);
			if(r != SASL_OK)
				return false;
			r = sasl_setprop(con, SASL_AUTH_EXTERNAL, &authid);
			if(r != SASL_OK)
				return false;
		}

		return true;
	}
Exemplo n.º 2
0
static int
__pmAuthServerSetProperties(sasl_conn_t *conn, int ssf)
{
    int saslsts;
    sasl_security_properties_t props;

    /* set external security strength factor */
    saslsts = sasl_setprop(conn, SASL_SSF_EXTERNAL, &ssf);
    if (saslsts != SASL_OK && saslsts != SASL_CONTINUE) {
	pmNotifyErr(LOG_ERR, "SASL setting external SSF to %d: %s",
			ssf, sasl_errstring(saslsts, NULL, NULL));
	return __pmSecureSocketsError(saslsts);
    }

    /* set general security properties */
    memset(&props, 0, sizeof(props));
    props.maxbufsize = LIMIT_AUTH_PDU;
    props.max_ssf = UINT_MAX;
    saslsts = sasl_setprop(conn, SASL_SEC_PROPS, &props);
    if (saslsts != SASL_OK && saslsts != SASL_CONTINUE) {
	pmNotifyErr(LOG_ERR, "SASL setting security properties: %s",
			sasl_errstring(saslsts, NULL, NULL));
	return __pmSecureSocketsError(saslsts);
    }

    return 0;
}
Exemplo n.º 3
0
int
ArgusInitializeAuthentication (struct ARGUS_INPUT *input)
{
   int retn = 1;

#ifdef ARGUS_SASL
   struct sockaddr_in localaddr, remoteaddr;
   int salen, fd = input->fd;
   char *localhostname = NULL;

   if ((retn = sasl_client_init(RaCallBacks)) != SASL_OK)
      ArgusLog (LOG_ERR, "ArgusInitializeAuthentication() sasl_client_init %d", retn);

   localhostname = ArgusCalloc (1, 1024);
   gethostname(localhostname, 1024);
   if (!strchr (localhostname, '.')) {
      strcat (localhostname, ".");
      getdomainname (&localhostname[strlen(localhostname)], 1024 - strlen(localhostname));
   }

   if ((retn = sasl_client_new("argus", localhostname, NULL, SASL_SECURITY_LAYER, &input->sasl_conn)) != SASL_OK)
      ArgusLog (LOG_ERR, "ArgusInitializeAuthentication() sasl_client_new %d", retn);
   
   /* set external properties here
   sasl_setprop(input->sasl_conn, SASL_SSF_EXTERNAL, &extprops); */
   
   /* set required security properties here
   sasl_setprop(input->sasl_conn, SASL_SEC_PROPS, &secprops); */
   
   /* set ip addresses */
   salen = sizeof(localaddr);
   if (getsockname(fd, (struct sockaddr *)&localaddr, &salen) < 0)
      perror("getsockname");

   salen = sizeof(remoteaddr); 
   if (getpeername(fd, (struct sockaddr *)&remoteaddr, &salen) < 0)
      perror("getpeername");

   if ((retn = sasl_setprop(input->sasl_conn, SASL_IP_LOCAL, &localaddr)) != SASL_OK)
      ArgusLog (LOG_ERR, "ArgusInitializeAuthentication() error setting localaddr %d", retn);

   if ((retn = sasl_setprop(input->sasl_conn, SASL_IP_REMOTE, &remoteaddr)) != SASL_OK)
      ArgusLog (LOG_ERR, "ArgusInitializeAuthentication() error setting remoteaddr %d", retn);

   retn = 1;
#endif 

#ifdef ARGUSDEBUG
   ArgusDebug (2, "ArgusInitializeAuthentication () returning %d\n", retn);
#endif 

   return (retn);
}
Exemplo n.º 4
0
EXPORTED int backend_starttls(	struct backend *s,
				struct tls_cmd_t *tls_cmd,
				const char *c_cert_file,
				const char *c_key_file)
{
#ifndef HAVE_SSL
    return -1;
#else
    char *auth_id = NULL;
    int *layerp = NULL;
    int r = 0;

    if (tls_cmd) {
	char buf[2048];
	/* send starttls command */
	prot_printf(s->out, "%s\r\n", tls_cmd->cmd);
	prot_flush(s->out);

	/* check response */
	if (!prot_fgets(buf, sizeof(buf), s->in) ||
	    strncmp(buf, tls_cmd->ok, strlen(tls_cmd->ok)))
	    return -1;
    }

    r = tls_init_clientengine(5, c_cert_file, c_key_file);
    if (r == -1) return -1;

    /* SASL and openssl have different ideas about whether ssf is signed */
    layerp = (int *) &s->ext_ssf;
    r = tls_start_clienttls(s->in->fd, s->out->fd, layerp, &auth_id,
			    &s->tlsconn, &s->tlssess);
    if (r == -1) return -1;

    if (s->saslconn) {
	r = sasl_setprop(s->saslconn, SASL_SSF_EXTERNAL, &s->ext_ssf);
	if (r == SASL_OK)
	    r = sasl_setprop(s->saslconn, SASL_AUTH_EXTERNAL, auth_id);
	if (auth_id) free(auth_id);
	if (r != SASL_OK) return -1;
    }

    prot_settls(s->in,  s->tlsconn);
    prot_settls(s->out, s->tlsconn);

    ask_capability(s, /*dobanner*/1, tls_cmd->auto_capa);

    return 0;
#endif /* HAVE_SSL */
}
Exemplo n.º 5
0
/* cyrussasl.setprop(conn, propnum, val)
 *
 * conn: the conn pointer from cyrussasl.server_new().
 * propnum: an integer corresponding to the property to set
 * val: a lua string object
 *
 * Throws Lua errors if it fails (as it should not typically fail).
 * Does not return any value.
 */
static int cyrussasl_sasl_setprop(lua_State *l)
{
  int err;
  int proptype;
  const void *proparg;
  struct _sasl_ctx *ctx = NULL;

  int numargs = lua_gettop(l);
  if (numargs != 3) {
    lua_pushstring(l, "usage: cyrussasl.setprop(conn, propnum, propval)");
    lua_error(l);
    return 0;
  }

  ctx      = get_context(l, 1);
  proptype = tointeger(l, 2);
  proparg  = tolstring(l, 3, NULL);

  err = sasl_setprop(ctx->conn, proptype, &proparg);
  if ( err != SASL_OK ) {
    const char *ret = get_context_message(ctx);
    if (ret)
      lua_pushstring(l, ret);
    else
      lua_pushstring(l, "sasl_setprop failed");
    lua_error(l);
    return 0;
  }

  return 0;
}
Exemplo n.º 6
0
/* cyrussasl.setssf(conn, min_ssf, max_ssf)
 *
 * conn: the conn pointer from cyrussasl.server_new().
 * min_ssf, max_ssf: set the minimum and maximum security strength factor 
 *                   required for this AuthN.
 *
 * Throws Lua errors if it fails (as it should not typically fail).
 * Does not return any value.
 */
static int cyrussasl_setssf(lua_State *l)
{
  sasl_security_properties_t secprops;
  int err;
  int min_ssf, max_ssf;
  struct _sasl_ctx *ctx = NULL;

  int numargs = lua_gettop(l);
  if (numargs != 3) {
    lua_pushstring(l, "usage: cyrussasl.setssf(conn, min_ssf, max_ssf)");
    lua_error(l);
    return 0;
  }

  ctx     = get_context(l, 1);
  min_ssf = tointeger(l, 2);
  max_ssf = tointeger(l, 3);

  memset(&secprops, 0L, sizeof(secprops));
  secprops.min_ssf = min_ssf;
  secprops.max_ssf = max_ssf;

  err = sasl_setprop(ctx->conn, SASL_SEC_PROPS, &secprops);
  if ( err != SASL_OK ) {
    lua_pushstring(l, "setssf failed");
    lua_error(l);
    return 0;
  }

  return 0;
}
Exemplo n.º 7
0
int virNetSASLSessionSecProps(virNetSASLSessionPtr sasl,
                              int minSSF,
                              int maxSSF,
                              bool allowAnonymous)
{
    sasl_security_properties_t secprops;
    int err;
    int ret = -1;

    VIR_DEBUG("minSSF=%d maxSSF=%d allowAnonymous=%d maxbufsize=%zu",
              minSSF, maxSSF, allowAnonymous, sasl->maxbufsize);

    virObjectLock(sasl);
    memset(&secprops, 0, sizeof(secprops));

    secprops.min_ssf = minSSF;
    secprops.max_ssf = maxSSF;
    secprops.maxbufsize = sasl->maxbufsize;
    secprops.security_flags = allowAnonymous ? 0 :
        SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;

    err = sasl_setprop(sasl->conn, SASL_SEC_PROPS, &secprops);
    if (err != SASL_OK) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot set security props %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
        goto cleanup;
    }

    ret = 0;

cleanup:
    virObjectUnlock(sasl);
    return ret;
}
Exemplo n.º 8
0
bool pni_init_client(pn_transport_t* transport) {
  pni_sasl_t *sasl = transport->sasl;
  int result;
  sasl_conn_t *cyrus_conn = NULL;
  do {
    if (sasl->config_dir) {
      result = sasl_set_path(SASL_PATH_TYPE_CONFIG, sasl->config_dir);
      if (result!=SASL_OK) break;
    }

    result = sasl_client_init(NULL);
    if (result!=SASL_OK) break;

    const sasl_callback_t *callbacks = sasl->username ? sasl->password ? pni_user_password_callbacks : pni_user_callbacks : NULL;
    result = sasl_client_new(amqp_service,
                             sasl->remote_fqdn,
                             NULL, NULL,
                             callbacks, 0,
                             &cyrus_conn);
    if (result!=SASL_OK) break;
    sasl->impl_context = cyrus_conn;

    sasl_security_properties_t secprops = {0};
    secprops.security_flags =
      ( sasl->allow_insecure_mechs ? 0 : SASL_SEC_NOPLAINTEXT ) |
      ( transport->auth_required ? SASL_SEC_NOANONYMOUS : 0 ) ;
    secprops.min_ssf = 0;
    secprops.max_ssf = 2048;
    secprops.maxbufsize = PN_SASL_MAX_BUFFSIZE;

    result = sasl_setprop(cyrus_conn, SASL_SEC_PROPS, &secprops);
    if (result!=SASL_OK) break;

    sasl_ssf_t ssf = sasl->external_ssf;
    result = sasl_setprop(cyrus_conn, SASL_SSF_EXTERNAL, &ssf);
    if (result!=SASL_OK) break;

    const char *extid = sasl->external_auth;
    if (extid) {
      result = sasl_setprop(cyrus_conn, SASL_AUTH_EXTERNAL, extid);
    }
  } while (false);
  cyrus_conn = (sasl_conn_t*) sasl->impl_context;
  return pni_check_sasl_result(cyrus_conn, result, transport);
}
Exemplo n.º 9
0
static int do_starttls(struct backend *s)
{
#ifndef HAVE_SSL
    return -1;
#else
    const struct tls_cmd_t *tls_cmd = &s->prot->tls_cmd;
    char buf[2048];
    int r;
    int *layerp;
    char *auth_id;
    sasl_ssf_t ssf;

    /* send starttls command */
    prot_printf(s->out, "%s\r\n", tls_cmd->cmd);
    prot_flush(s->out);

    /* check response */
    if (!prot_fgets(buf, sizeof(buf), s->in) ||
	strncmp(buf, tls_cmd->ok, strlen(tls_cmd->ok)))
	return -1;

    r = tls_init_clientengine(5, "", "");
    if (r == -1) return -1;

    /* SASL and openssl have different ideas about whether ssf is signed */
    layerp = (int *) &ssf;
    r = tls_start_clienttls(s->in->fd, s->out->fd, layerp, &auth_id,
			    &s->tlsconn, &s->tlssess);
    if (r == -1) return -1;

    r = sasl_setprop(s->saslconn, SASL_SSF_EXTERNAL, &ssf);
    if (r == SASL_OK)
	r = sasl_setprop(s->saslconn, SASL_AUTH_EXTERNAL, auth_id);
    if (auth_id) free(auth_id);
    if (r != SASL_OK) return -1;

    prot_settls(s->in,  s->tlsconn);
    prot_settls(s->out, s->tlsconn);

    ask_capability(s, /*dobanner*/1, s->prot->tls_cmd.auto_capa);

    return 0;
#endif /* HAVE_SSL */
}
Exemplo n.º 10
0
/*
 * per SASL session initialization.
 * We keep session data in Connection layer.
 */
DWORD
VmDirSASLSessionInit(
    PVDIR_SASL_BIND_INFO    pSaslBindInfo
    )
{
    DWORD               dwError = 0;
    sasl_conn_t*        pLocalSaslCtx = NULL;

    // start new sasl context
    dwError = sasl_server_new(  "ldap", // service protocol
                                NULL,   // need realm here?
                                NULL,
                                NULL,
                                NULL,
                                NULL,   // per session CB
                                0,      // default security flag
                                &pLocalSaslCtx);
    BAIL_ON_SASL_ERROR(dwError)


    // default security properties
    dwError = sasl_setprop( pLocalSaslCtx,
                            SASL_SEC_PROPS,
                            &gSASLSecurityProps );
    BAIL_ON_SASL_ERROR(dwError);

    pSaslBindInfo->pSaslCtx   = pLocalSaslCtx;

cleanup:

    return dwError;

sasl_error:

    if (pLocalSaslCtx)
    {
        sasl_dispose(&pLocalSaslCtx);
    }

    memset(pSaslBindInfo, 0, sizeof(*pSaslBindInfo));

    VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "SASLSessionInit: sasl error (%d)", dwError );

    dwError = _VmDirSASLToLDAPError(dwError);

    goto cleanup;
}
Exemplo n.º 11
0
int virNetSASLSessionExtKeySize(virNetSASLSessionPtr sasl,
                                int ssf)
{
    int err;
    int ret = -1;
    virObjectLock(sasl);

    err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
    if (err != SASL_OK) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot set external SSF %d (%s)"),
                       err, sasl_errstring(err, NULL, NULL));
        goto cleanup;
    }

    ret = 0;

cleanup:
    virObjectUnlock(sasl);
    return ret;
}
static int xsasl_cyrus_server_set_security(XSASL_SERVER *xp,
					           const char *sasl_opts_val)
{
    XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
    sasl_security_properties_t sec_props;
    int     sasl_status;

    /*
     * Security options. Some information can be found in the sasl.h include
     * file.
     */
    memset(&sec_props, 0, sizeof(sec_props));
    sec_props.min_ssf = 0;
    sec_props.max_ssf = 0;			/* don't allow real SASL
						 * security layer */
    if (*sasl_opts_val == 0) {
	sec_props.security_flags = 0;
    } else {
	sec_props.security_flags =
	    xsasl_cyrus_security_parse_opts(sasl_opts_val);
	if (sec_props.security_flags == 0) {
	    msg_warn("bad per-session SASL security properties");
	    return (XSASL_AUTH_FAIL);
	}
    }
    sec_props.maxbufsize = 0;
    sec_props.property_names = 0;
    sec_props.property_values = 0;

    if ((sasl_status = sasl_setprop(server->sasl_conn, SASL_SEC_PROPS,
				    &sec_props)) != SASL_OK) {
	msg_warn("SASL per-connection security setup; %s",
		 xsasl_cyrus_strerror(sasl_status));
	return (XSASL_AUTH_FAIL);
    }
    return (XSASL_AUTH_OK);
}
Exemplo n.º 13
0
static int xsasl_cyrus_client_set_security(XSASL_CLIENT *xp,
					           const char *sasl_opts_val)
{
    XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
    sasl_security_properties_t sec_props;
    int     sasl_status;

    /*
     * Per-session security properties. XXX This routine is not sufficiently
     * documented. What is the purpose of all this?
     */
    memset(&sec_props, 0, sizeof(sec_props));
    sec_props.min_ssf = 0;
    sec_props.max_ssf = 0;			/* don't allow real SASL
						 * security layer */
    if (*sasl_opts_val == 0) {
	sec_props.security_flags = 0;
    } else {
	sec_props.security_flags =
	    xsasl_cyrus_security_parse_opts(sasl_opts_val);
	if (sec_props.security_flags == 0) {
	    msg_warn("bad per-session SASL security properties");
	    return (XSASL_AUTH_FAIL);
	}
    }
    sec_props.maxbufsize = 0;
    sec_props.property_names = 0;
    sec_props.property_values = 0;
    if ((sasl_status = sasl_setprop(client->sasl_conn, SASL_SEC_PROPS,
				    &sec_props)) != SASL_OK) {
	msg_warn("set per-session SASL security properties: %s",
		 xsasl_cyrus_strerror(sasl_status));
	return (XSASL_AUTH_FAIL);
    }
    return (XSASL_AUTH_OK);
}
Exemplo n.º 14
0
static int backend_authenticate(struct backend *s, const char *userid,
				sasl_callback_t *cb, const char **status)
{
    struct protocol_t *prot = s->prot;
    int r;
    char *mechlist;
    sasl_security_properties_t secprops =
	{ 0, 0xFF, PROT_BUFSIZE, 0, NULL, NULL }; /* default secprops */
    struct sockaddr_storage saddr_l, saddr_r;
    char remoteip[60], localip[60];
    socklen_t addrsize;
    int local_cb = 0;
    char buf[2048], optstr[128], *p;
    const char *mech_conf, *pass;

    /* set the IP addresses */
    addrsize=sizeof(struct sockaddr_storage);
    if (getpeername(s->sock, (struct sockaddr *)&saddr_r, &addrsize) != 0)
	return SASL_FAIL;
    if(iptostring((struct sockaddr *)&saddr_r, addrsize, remoteip, 60) != 0)
	return SASL_FAIL;
  
    addrsize=sizeof(struct sockaddr_storage);
    if (getsockname(s->sock, (struct sockaddr *)&saddr_l, &addrsize)!=0)
	return SASL_FAIL;
    if(iptostring((struct sockaddr *)&saddr_l, addrsize, localip, 60) != 0)
	return SASL_FAIL;

    if (!cb) {
	local_cb = 1;
	strlcpy(optstr, s->hostname, sizeof(optstr));
	p = strchr(optstr, '.');
	if (p) *p = '\0';
	strlcat(optstr, "_password", sizeof(optstr));
	pass = config_getoverflowstring(optstr, NULL);
	if(!pass) pass = config_getstring(IMAPOPT_PROXY_PASSWORD);
	cb = mysasl_callbacks(userid, 
			      config_getstring(IMAPOPT_PROXY_AUTHNAME),
			      config_getstring(IMAPOPT_PROXY_REALM),
			      pass);
    }

    /* Require proxying if we have an "interesting" userid (authzid) */
    r = sasl_client_new(prot->sasl_service, s->hostname, localip, remoteip, cb,
			(userid  && *userid ? SASL_NEED_PROXY : 0) |
			(prot->sasl_cmd.parse_success ? SASL_SUCCESS_DATA : 0),
			&s->saslconn);
    if (r != SASL_OK)
	goto out;

    r = sasl_setprop(s->saslconn, SASL_SEC_PROPS, &secprops);
    if (r != SASL_OK)
	goto out;

    /* Get SASL mechanism list.  We can force a particular
       mechanism using a <shorthost>_mechs option */
    strcpy(buf, s->hostname);
    p = strchr(buf, '.');
    if (p) *p = '\0';
    strcat(buf, "_mechs");
    mech_conf = config_getoverflowstring(buf, NULL);

    if (!mech_conf) {
	mech_conf = config_getstring(IMAPOPT_FORCE_SASL_CLIENT_MECH);
    }

    mechlist = backend_get_cap_params(s, CAPA_AUTH);

    do {
	/* If we have a mech_conf, use it */
	if (mech_conf && mechlist) {
	    char *conf = xstrdup(mech_conf);
	    char *newmechlist = intersect_mechlists( conf, mechlist );

	    if ( newmechlist == NULL ) {
		syslog( LOG_INFO, "%s did not offer %s", s->hostname,
			mech_conf );
	    }

	    free(conf);
	    free(mechlist);
	    mechlist = newmechlist;
	}

	if (mechlist) {
	    /* we now do the actual SASL exchange */
	    saslclient(s->saslconn, &prot->sasl_cmd, mechlist,
		       s->in, s->out, &r, status);

	    /* garbage collect */
	    free(mechlist);
	    mechlist = NULL;
	}
	else r = SASL_NOMECH;

	/* If we don't have a usable mech, do TLS and try again */
    } while (r == SASL_NOMECH &&
	     CAPA(s, CAPA_STARTTLS) &&
	     do_starttls(s) != -1 &&
	     (mechlist = backend_get_cap_params(s, CAPA_AUTH)));

    if (r == SASL_OK) {
	prot_setsasl(s->in, s->saslconn);
	prot_setsasl(s->out, s->saslconn);
    }

    if (mechlist) free(mechlist);

out:
    /* r == SASL_OK on success */
    if (local_cb) free_callbacks(cb);
    return r;
}
Exemplo n.º 15
0
int
auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
{
auth_cyrus_sasl_options_block *ob =
  (auth_cyrus_sasl_options_block *)(ablock->options_block);
uschar *output, *out2, *input, *clear, *hname;
uschar *debug = NULL;   /* Stops compiler complaining */
sasl_callback_t cbs[] = {{SASL_CB_LIST_END, NULL, NULL}};
sasl_conn_t *conn;
char * realm_expanded = NULL;
int rc, firsttime = 1, clen, *negotiated_ssf_ptr = NULL, negotiated_ssf;
unsigned int inlen, outlen;

input = data;
inlen = Ustrlen(data);

HDEBUG(D_auth) debug = string_copy(data);

hname = expand_string(ob->server_hostname);
if (hname && ob->server_realm)
  realm_expanded = CS expand_string(ob->server_realm);
if (!hname  ||  !realm_expanded  && ob->server_realm)
  {
  auth_defer_msg = expand_string_message;
  return DEFER;
  }

if (inlen)
  {
  if ((clen = b64decode(input, &clear)) < 0)
    return BAD64;
  input = clear;
  inlen = clen;
  }

if ((rc = sasl_server_init(cbs, "exim")) != SASL_OK)
  {
  auth_defer_msg = US"couldn't initialise Cyrus SASL library";
  return DEFER;
  }

rc = sasl_server_new(CS ob->server_service, CS hname, realm_expanded, NULL,
  NULL, NULL, 0, &conn);

HDEBUG(D_auth)
  debug_printf("Initialised Cyrus SASL server connection; service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
      ob->server_service, hname, realm_expanded);

if (rc != SASL_OK )
  {
  auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
  sasl_done();
  return DEFER;
  }

if (tls_in.cipher)
  {
  if ((rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, (sasl_ssf_t *) &tls_in.bits)) != SASL_OK)
    {
    HDEBUG(D_auth) debug_printf("Cyrus SASL EXTERNAL SSF set %d failed: %s\n",
        tls_in.bits, sasl_errstring(rc, NULL, NULL));
    auth_defer_msg = US"couldn't set Cyrus SASL EXTERNAL SSF";
    sasl_done();
    return DEFER;
    }
  else
    HDEBUG(D_auth) debug_printf("Cyrus SASL set EXTERNAL SSF to %d\n", tls_in.bits);
  }
else
  HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n");

/* So sasl_setprop() documents non-shorted IPv6 addresses which is incredibly
annoying; looking at cyrus-imapd-2.3.x source, the IP address is constructed
with their iptostring() function, which just wraps
getnameinfo(..., NI_NUMERICHOST|NI_NUMERICSERV), which is equivalent to the
inet_ntop which we wrap in our host_ntoa() function.

So the docs are too strict and we shouldn't worry about :: contractions. */

/* Set properties for remote and local host-ip;port */
for (int i = 0; i < 2; ++i)
  {
  struct sockaddr_storage ss;
  int (*query)(int, struct sockaddr *, socklen_t *);
  int propnum, port;
  const uschar *label;
  uschar *address, *address_port;
  const char *s_err;
  socklen_t sslen;

  if (i)
    {
    query = &getpeername;
    propnum = SASL_IPREMOTEPORT;
    label = CUS"peer";
    }
  else
    {
    query = &getsockname;
    propnum = SASL_IPLOCALPORT;
    label = CUS"local";
    }

  sslen = sizeof(ss);
  if ((rc = query(fileno(smtp_in), (struct sockaddr *) &ss, &sslen)) < 0)
    {
    HDEBUG(D_auth)
      debug_printf("Failed to get %s address information: %s\n",
          label, strerror(errno));
    break;
    }

  address = host_ntoa(-1, &ss, NULL, &port);
  address_port = string_sprintf("%s;%d", address, port);

  if ((rc = sasl_setprop(conn, propnum, address_port)) != SASL_OK)
    {
    s_err = sasl_errdetail(conn);
    HDEBUG(D_auth)
      debug_printf("Failed to set %s SASL property: [%d] %s\n",
          label, rc, s_err ? s_err : "<unknown reason>");
    break;
    }
  HDEBUG(D_auth) debug_printf("Cyrus SASL set %s hostport to: %s\n",
      label, address_port);
  }

for (rc = SASL_CONTINUE; rc == SASL_CONTINUE; )
  {
  if (firsttime)
    {
    firsttime = 0;
    HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
    rc = sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
           (const char **)(&output), &outlen);
    }
  else
    {
    /* make sure that we have a null-terminated string */
    out2 = string_copyn(output, outlen);

    if ((rc = auth_get_data(&input, out2, outlen)) != OK)
      {
      /* we couldn't get the data, so free up the library before
       * returning whatever error we get */
      sasl_dispose(&conn);
      sasl_done();
      return rc;
      }
    inlen = Ustrlen(input);

    HDEBUG(D_auth) debug = string_copy(input);
    if (inlen)
      {
      if ((clen = b64decode(input, &clear)) < 0)
       {
       sasl_dispose(&conn);
       sasl_done();
       return BAD64;
       }
      input = clear;
      inlen = clen;
      }

    HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
    rc = sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
    }

  if (rc == SASL_BADPROT)
    {
    sasl_dispose(&conn);
    sasl_done();
    return UNEXPECTED;
    }
  if (rc == SASL_CONTINUE)
    continue;

  /* Get the username and copy it into $auth1 and $1. The former is now the
  preferred variable; the latter is the original variable. */

  if ((sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2))) != SASL_OK)
    {
    HDEBUG(D_auth)
      debug_printf("Cyrus SASL library will not tell us the username: %s\n",
	  sasl_errstring(rc, NULL, NULL));
    log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
       "Cyrus SASL username fetch problem: %s", ablock->name, ob->server_mech,
       sasl_errstring(rc, NULL, NULL));
    sasl_dispose(&conn);
    sasl_done();
    return FAIL;
    }
  auth_vars[0] = expand_nstring[1] = string_copy(out2);
  expand_nlength[1] = Ustrlen(out2);
  expand_nmax = 1;

  switch (rc)
    {
    case SASL_FAIL: case SASL_BUFOVER: case SASL_BADMAC: case SASL_BADAUTH:
    case SASL_NOAUTHZ: case SASL_ENCRYPT: case SASL_EXPIRED:
    case SASL_DISABLED: case SASL_NOUSER:
      /* these are considered permanent failure codes */
      HDEBUG(D_auth)
	debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
      log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
	 "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
	 sasl_errstring(rc, NULL, NULL));
      sasl_dispose(&conn);
      sasl_done();
      return FAIL;

    case SASL_NOMECH:
      /* this is a temporary failure, because the mechanism is not
       * available for this user. If it wasn't available at all, we
       * shouldn't have got here in the first place...
       */
      HDEBUG(D_auth)
	debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
      auth_defer_msg =
	  string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
      sasl_dispose(&conn);
      sasl_done();
      return DEFER;

    case SASL_OK:
      HDEBUG(D_auth)
	debug_printf("Cyrus SASL %s authentication succeeded for %s\n",
	    ob->server_mech, auth_vars[0]);

      if ((rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf_ptr)))!= SASL_OK)
	{
	HDEBUG(D_auth)
	  debug_printf("Cyrus SASL library will not tell us the SSF: %s\n",
	      sasl_errstring(rc, NULL, NULL));
	log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
	    "Cyrus SASL SSF value not available: %s", ablock->name, ob->server_mech,
	    sasl_errstring(rc, NULL, NULL));
	sasl_dispose(&conn);
	sasl_done();
	return FAIL;
	}
      negotiated_ssf = *negotiated_ssf_ptr;
      HDEBUG(D_auth)
	debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf);
      if (negotiated_ssf > 0)
	{
	HDEBUG(D_auth)
	  debug_printf("Exim does not implement SASL wrapping (needed for SSF %d).\n", negotiated_ssf);
	log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
	    "Cyrus SASL SSF %d not supported by Exim", ablock->name, ob->server_mech, negotiated_ssf);
	sasl_dispose(&conn);
	sasl_done();
	return FAIL;
	}

      /* close down the connection, freeing up library's memory */
      sasl_dispose(&conn);
      sasl_done();

      /* Expand server_condition as an authorization check */
      return auth_check_serv_cond(ablock);

    default:
      /* Anything else is a temporary failure, and we'll let SASL print out
       * the error string for us
       */
      HDEBUG(D_auth)
	debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
      auth_defer_msg =
	  string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
      sasl_dispose(&conn);
      sasl_done();
      return DEFER;
    }
  }
/* NOTREACHED */
return 0;  /* Stop compiler complaints */
}
Exemplo n.º 16
0
/* mutt_sasl_client_new: wrapper for sasl_client_new which also sets various
 * security properties. If this turns out to be fine for POP too we can
 * probably stop exporting mutt_sasl_get_callbacks(). */
int mutt_sasl_client_new (CONNECTION * conn, sasl_conn_t ** saslconn)
{
  sasl_security_properties_t secprops;

  struct sockaddr_storage local, remote;
  socklen_t size;
  char iplocalport[IP_PORT_BUFLEN], ipremoteport[IP_PORT_BUFLEN];
  const char *service;
  int rc;

  if (mutt_sasl_start () != SASL_OK)
    return -1;

  switch (conn->account.type) {
  case M_ACCT_TYPE_IMAP:
    service = "imap";
    break;
  case M_ACCT_TYPE_POP:
    service = "pop";
    break;
  default:
    debug_print (1, ("account type unset\n"));
    return -1;
  }

  size = sizeof (local);
  if (getsockname (conn->fd, (struct sockaddr *) &local, &size)) {
    debug_print (1, ("getsockname for local failed\n"));
    return -1;
  }
  else
    if (iptostring
        ((struct sockaddr *) &local, size, iplocalport,
         IP_PORT_BUFLEN) != SASL_OK) {
    debug_print (1, ("iptostring for local failed\n"));
    return -1;
  }

  size = sizeof (remote);
  if (getpeername (conn->fd, (struct sockaddr *) &remote, &size)) {
    debug_print (1, ("getsockname for remote failed\n"));
    return -1;
  }
  else
    if (iptostring
        ((struct sockaddr *) &remote, size, ipremoteport,
         IP_PORT_BUFLEN) != SASL_OK) {
    debug_print (1, ("iptostring for remote failed\n"));
    return -1;
  }

  debug_print (1, ("local ip: %s, remote ip:%s\n", iplocalport, ipremoteport));

  rc =
    sasl_client_new (service, conn->account.host, iplocalport, ipremoteport,
                     mutt_sasl_get_callbacks (&conn->account), 0, saslconn);

  if (rc != SASL_OK) {
    debug_print (1, ("Error allocating SASL connection\n"));
    return -1;
  }

  /*** set sasl IP properties, necessary for use with krb4 ***/
  /* Do we need to fail if this fails? I would assume having these unset
   * would just disable KRB4. Who wrote this code?
   */
  {
    struct sockaddr_in local, remote;
    socklen_t size;

    size = sizeof (local);
    if (getsockname (conn->fd, (struct sockaddr *) &local, &size))
      return -1;

    size = sizeof (remote);
    if (getpeername (conn->fd, (struct sockaddr *) &remote, &size))
      return -1;

#ifdef SASL_IP_LOCAL
    if (sasl_setprop (*saslconn, SASL_IP_LOCAL, &local) != SASL_OK) {
      debug_print (1, ("Error setting local IP address\n"));
      return -1;
    }
#endif

#ifdef SASL_IP_REMOTE
    if (sasl_setprop (*saslconn, SASL_IP_REMOTE, &remote) != SASL_OK) {
      debug_print (1, ("Error setting remote IP address\n"));
      return -1;
    }
#endif
  }

  /* set security properties. We use NOPLAINTEXT globally, since we can
   * just fall back to LOGIN in the IMAP case anyway. If that doesn't
   * work for POP, we can make it a flag or move this code into
   * imap/auth_sasl.c */
  memset (&secprops, 0, sizeof (secprops));
  /* Work around a casting bug in the SASL krb4 module */
  secprops.max_ssf = 0x7fff;
  secprops.maxbufsize = M_SASL_MAXBUF;
  secprops.security_flags |= SASL_SEC_NOPLAINTEXT;
  if (sasl_setprop (*saslconn, SASL_SEC_PROPS, &secprops) != SASL_OK) {
    debug_print (1, ("Error setting security properties\n"));
    return -1;
  }

  if (conn->ssf) {
    debug_print (2, ("External SSF: %d\n", conn->ssf));
    if (sasl_setprop (*saslconn, SASL_SSF_EXTERNAL, &(conn->ssf)) != SASL_OK)
    {
      debug_print (1, ("Error setting external properties\n"));
      return -1;
    }
    debug_print (2, ("External authentication name: %s\n", conn->account.user));
    if (sasl_setprop (*saslconn, SASL_AUTH_EXTERNAL, conn->account.user) !=
        SASL_OK) {
      debug_print (1, ("Error setting external properties\n"));
      return -1;
    }
  }

  return 0;
}
Exemplo n.º 17
0
/*
 * Initialize SASL and set necessary options
 */
int init_sasl(isieve_t *obj,
	      int ssf,
	      sasl_callback_t *callbacks)
{
  static int sasl_started = 0;
  int saslresult = SASL_OK;
  sasl_security_properties_t *secprops=NULL;
  socklen_t addrsize=sizeof(struct sockaddr_storage);
  struct sockaddr_storage saddr_l, saddr_r;
  char localip[60], remoteip[60];

  /* attempt to start sasl */
  if(!sasl_started) {
      saslresult=sasl_client_init(NULL);
      obj->conn = NULL;
      sasl_started = 1;
  }

  /* Save the callbacks array */
  obj->callbacks = callbacks;

  if (saslresult!=SASL_OK) return -1;

  addrsize=sizeof(struct sockaddr_storage);
  if (getpeername(obj->sock,(struct sockaddr *)&saddr_r,&addrsize)!=0)
      return -1;
  
  addrsize=sizeof(struct sockaddr_storage);
  if (getsockname(obj->sock,(struct sockaddr *)&saddr_l,&addrsize)!=0)
      return -1;
#if 0
  /* XXX  The following line causes problems with KERBEROS_V4 decoding.
   * We're not sure why its an issue, but this code isn't used in any of 
   * our other client code (imtest.c, backend.c), so we're removing it.
   */
  /* set the port manually since getsockname is stupid and doesn't */
  ((struct sockaddr_in *)&saddr_l)->sin_port = htons(obj->port);
#endif
  if (iptostring((struct sockaddr *)&saddr_r, addrsize, remoteip, 60))
      return -1;

  if (iptostring((struct sockaddr *)&saddr_l, addrsize, localip, 60))
      return -1;

  if(obj->conn) sasl_dispose(&obj->conn);

  /* client new connection */
  saslresult=sasl_client_new(SIEVE_SERVICE_NAME,
			     obj->serverFQDN,
			     localip, remoteip,
			     callbacks,
			     SASL_SUCCESS_DATA,
			     &obj->conn);

  if (saslresult!=SASL_OK) return -1;

  /* create a security structure and give it to sasl */
  secprops = make_secprops(0, ssf);
  if (secprops != NULL)
  {
    sasl_setprop(obj->conn, SASL_SEC_PROPS, secprops);
    free(secprops);
  }

  return 0;
}
Exemplo n.º 18
0
static JabberSaslState
jabber_auth_start_cyrus(JabberStream *js, xmlnode **reply, char **error)
{
	PurpleAccount *account;
	const char *clientout = NULL;
	char *enc_out;
	unsigned coutlen = 0;
	sasl_security_properties_t secprops;
	gboolean again;
	gboolean plaintext = TRUE;

	/* Set up security properties and options */
	secprops.min_ssf = 0;
	secprops.security_flags = SASL_SEC_NOANONYMOUS;

	account = purple_connection_get_account(js->gc);

	if (!jabber_stream_is_ssl(js)) {
		secprops.max_ssf = -1;
		secprops.maxbufsize = 4096;
		plaintext = purple_account_get_bool(account, "auth_plain_in_clear", FALSE);
		if (!plaintext)
			secprops.security_flags |= SASL_SEC_NOPLAINTEXT;
	} else {
		secprops.max_ssf = 0;
		secprops.maxbufsize = 0;
		plaintext = TRUE;
	}
	secprops.property_names = 0;
	secprops.property_values = 0;

	do {
		again = FALSE;

		js->sasl_state = sasl_client_new("xmpp", js->serverFQDN, NULL, NULL, js->sasl_cb, 0, &js->sasl);
		if (js->sasl_state==SASL_OK) {
			sasl_setprop(js->sasl, SASL_SEC_PROPS, &secprops);
			purple_debug_info("sasl", "Mechs found: %s\n", js->sasl_mechs->str);
			js->sasl_state = sasl_client_start(js->sasl, js->sasl_mechs->str, NULL, &clientout, &coutlen, &js->current_mech);
		}
		switch (js->sasl_state) {
			/* Success */
			case SASL_OK:
			case SASL_CONTINUE:
				break;
			case SASL_NOMECH:
				/* No mechanisms have offered to help */

				/* Firstly, if we don't have a password try
				 * to get one
				 */

				if (!purple_account_get_password(account)) {
					purple_account_request_password(account, G_CALLBACK(auth_pass_cb), G_CALLBACK(auth_no_pass_cb), js->gc);
					return JABBER_SASL_STATE_CONTINUE;

				/* If we've got a password, but aren't sending
				 * it in plaintext, see if we can turn on
				 * plaintext auth
				 */
				/* XXX Should we just check for PLAIN/LOGIN being offered mechanisms? */
				} else if (!plaintext) {
					char *msg = g_strdup_printf(_("%s may require plaintext authentication over an unencrypted connection.  Allow this and continue authentication?"),
							purple_account_get_username(account));
					purple_request_yes_no(js->gc, _("Plaintext Authentication"),
							_("Plaintext Authentication"),
							msg,
							1, account, NULL, NULL, account,
							allow_cyrus_plaintext_auth,
							disallow_plaintext_auth);
					g_free(msg);
					return JABBER_SASL_STATE_CONTINUE;

				} else
					js->auth_fail_count++;

				if (js->auth_fail_count == 1 &&
					(js->sasl_mechs->str && g_str_equal(js->sasl_mechs->str, "GSSAPI"))) {
					/* If we tried GSSAPI first, it failed, and it was the only method we had to try, try jabber:iq:auth
					 * for compatibility with iChat 10.5 Server and other jabberd based servers.
					 *
					 * iChat Server 10.5 and certain other corporate servers offer SASL GSSAPI by default, which is often
					 * not configured on the client side, and expects a fallback to jabber:iq:auth when it (predictably) fails.
					 *
					 * Note: xep-0078 points out that using jabber:iq:auth after a sasl failure is wrong. However,
					 * I believe this refers to actual authentication failure, not a simple lack of concordant mechanisms.
					 * Doing otherwise means that simply compiling with SASL support renders the client unable to connect to servers
					 * which would connect without issue otherwise. -evands
					 */
					js->auth_mech = NULL;
					jabber_auth_start_old(js);
					return JABBER_SASL_STATE_CONTINUE;
				}

				break;

				/* Fatal errors. Give up and go home */
			case SASL_BADPARAM:
			case SASL_NOMEM:
				*error = g_strdup(_("SASL authentication failed"));
				break;

				/* For everything else, fail the mechanism and try again */
			default:
				purple_debug_info("sasl", "sasl_state is %d, failing the mech and trying again\n", js->sasl_state);

				js->auth_fail_count++;

				/*
				 * DAA: is this right?
				 * The manpage says that "mech" will contain the chosen mechanism on success.
				 * Presumably, if we get here that isn't the case and we shouldn't try again?
				 * I suspect that this never happens.
				 */
				/*
				 * SXW: Yes, this is right. What this handles is the situation where a
				 * mechanism, say GSSAPI, is tried. If that mechanism fails, it may be
				 * due to mechanism specific issues, so we want to try one of the other
				 * supported mechanisms. This code handles that case
				 */
				if (js->current_mech && *js->current_mech) {
					char *pos;
					if ((pos = strstr(js->sasl_mechs->str, js->current_mech))) {
						g_string_erase(js->sasl_mechs, pos-js->sasl_mechs->str, strlen(js->current_mech));
					}
					/* Remove space which separated this mech from the next */
					if ((js->sasl_mechs->str)[0] == ' ') {
						g_string_erase(js->sasl_mechs, 0, 1);
					}
					again = TRUE;
				}

				sasl_dispose(&js->sasl);
		}
	} while (again);

	if (js->sasl_state == SASL_CONTINUE || js->sasl_state == SASL_OK) {
		xmlnode *auth = xmlnode_new("auth");
		xmlnode_set_namespace(auth, NS_XMPP_SASL);
		xmlnode_set_attrib(auth, "mechanism", js->current_mech);

		xmlnode_set_attrib(auth, "xmlns:ga", "http://www.google.com/talk/protocol/auth");
		xmlnode_set_attrib(auth, "ga:client-uses-full-bind-result", "true");

		if (clientout) {
			if (coutlen == 0) {
				xmlnode_insert_data(auth, "=", -1);
			} else {
				enc_out = purple_base64_encode((unsigned char*)clientout, coutlen);
				xmlnode_insert_data(auth, enc_out, -1);
				g_free(enc_out);
			}
		}

		*reply = auth;
		return JABBER_SASL_STATE_CONTINUE;
	} else {
		return JABBER_SASL_STATE_FAIL;
	}
}
Exemplo n.º 19
0
int main(int argc, char *argv[])
{
    int c;
    char *port = "12345";
    char *service = "rcmd";
    char *hostname = NULL;
    int *l, maxfd=0;
    int r, i;
    sasl_conn_t *conn;
    int cb_flag = 0;

    while ((c = getopt(argc, argv, "Cch:p:s:m:")) != EOF) {
	switch(c) {
	case 'C':
	    cb_flag = 2;        /* channel bindings are critical */
	    break;

	case 'c':
	    cb_flag = 1;        /* channel bindings are present */
	    break;

	case 'h':
	    hostname = optarg;
	    break;

	case 'p':
	    port = optarg;
	    break;

	case 's':
	    service = optarg;
	    break;

	case 'm':
	    mech = optarg;
	    break;

	default:
	    usage();
	    break;
	}
    }

    /* initialize the sasl library */
    r = sasl_server_init(NULL, "sample");
    if (r != SASL_OK) saslfail(r, "initializing libsasl");

    /* get a listening socket */
    if ((l = listensock(port, PF_UNSPEC)) == NULL) {
	saslfail(SASL_FAIL, "allocating listensock");
    }

    for (i = 1; i <= l[0]; i++) {
       if (l[i] > maxfd)
           maxfd = l[i];
    }

    for (;;) {
	char localaddr[NI_MAXHOST | NI_MAXSERV],
	     remoteaddr[NI_MAXHOST | NI_MAXSERV];
	char myhostname[1024+1];
	char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
	struct sockaddr_storage local_ip, remote_ip;
	int niflags, error;
	socklen_t salen;
	int nfds, fd = -1;
	FILE *in, *out;
	fd_set readfds;
	sasl_channel_binding_t cb;

	FD_ZERO(&readfds);
	for (i = 1; i <= l[0]; i++)
	    FD_SET(l[i], &readfds);

	nfds = select(maxfd + 1, &readfds, 0, 0, 0);
	if (nfds <= 0) {
	    if (nfds < 0 && errno != EINTR)
		perror("select");
	    continue;
	}

       for (i = 1; i <= l[0]; i++) 
           if (FD_ISSET(l[i], &readfds)) {
               fd = accept(l[i], NULL, NULL);
               break;
           }

	if (fd < 0) {
	    if (errno != EINTR)
		perror("accept");
	    continue;
	}

	printf("accepted new connection\n");

	/* set ip addresses */
	salen = sizeof(local_ip);
	if (getsockname(fd, (struct sockaddr *)&local_ip, &salen) < 0) {
	    perror("getsockname");
	}
	niflags = (NI_NUMERICHOST | NI_NUMERICSERV);
#ifdef NI_WITHSCOPEID
	if (((struct sockaddr *)&local_ip)->sa_family == AF_INET6)
	    niflags |= NI_WITHSCOPEID;
#endif
	error = getnameinfo((struct sockaddr *)&local_ip, salen, hbuf,
			    sizeof(hbuf), pbuf, sizeof(pbuf), niflags);
	if (error != 0) {
	    fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error));
	    strcpy(hbuf, "unknown");
	    strcpy(pbuf, "unknown");
	}
        snprintf(localaddr, sizeof(localaddr), "%s;%s", hbuf, pbuf);

	salen = sizeof(remote_ip);
	if (getpeername(fd, (struct sockaddr *)&remote_ip, &salen) < 0) {
	    perror("getpeername");
	}

	niflags = (NI_NUMERICHOST | NI_NUMERICSERV);
#ifdef NI_WITHSCOPEID
	if (((struct sockaddr *)&remote_ip)->sa_family == AF_INET6)
	    niflags |= NI_WITHSCOPEID;
#endif
	error = getnameinfo((struct sockaddr *)&remote_ip, salen, hbuf,
			    sizeof(hbuf), pbuf, sizeof(pbuf), niflags);
	if (error != 0) {
	    fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error));
	    strcpy(hbuf, "unknown");
	    strcpy(pbuf, "unknown");
	}
	snprintf(remoteaddr, sizeof(remoteaddr), "%s;%s", hbuf, pbuf);

	if (hostname == NULL) {
	    r = gethostname(myhostname, sizeof(myhostname)-1);
	    if(r == -1) saslfail(r, "getting hostname");
	    hostname = myhostname;
	}

	r = sasl_server_new(service, hostname, NULL, localaddr, remoteaddr,
			    NULL, 0, &conn);
	if (r != SASL_OK) saslfail(r, "allocating connection state");

	cb.name = "sasl-sample";
	cb.critical = cb_flag > 1;
	cb.data = "this is a test of channel binding";
	cb.len = strlen(cb.data);

	if (cb_flag) {
	    sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb);
	}

	/* set external properties here
	sasl_setprop(conn, SASL_SSF_EXTERNAL, &extprops); */

	/* set required security properties here
	sasl_setprop(conn, SASL_SEC_PROPS, &secprops); */

	in = fdopen(fd, "r");
	out = fdopen(fd, "w");

	r = mysasl_negotiate(in, out, conn);
	if (r == SASL_OK) {
	    /* send/receive data */


	}

	printf("closing connection\n");
	fclose(in);
	fclose(out);
	close(fd);
	sasl_dispose(&conn);
    }

    sasl_done();
}
Exemplo n.º 20
0
/* mutt_sasl_client_new: wrapper for sasl_client_new which also sets various
 * security properties. If this turns out to be fine for POP too we can
 * probably stop exporting mutt_sasl_get_callbacks(). */
int mutt_sasl_client_new (CONNECTION* conn, sasl_conn_t** saslconn)
{
  sasl_security_properties_t secprops;
  struct sockaddr_storage local, remote;
  socklen_t size;
  char iplocalport[IP_PORT_BUFLEN], ipremoteport[IP_PORT_BUFLEN];
  char* plp = NULL;
  char* prp = NULL;
  const char* service;
  int rc;

  if (mutt_sasl_start () != SASL_OK)
    return -1;

  switch (conn->account.type)
  {
    case M_ACCT_TYPE_IMAP:
      service = "imap";
      break;
    case M_ACCT_TYPE_POP:
      service = "pop";
      break;
    case M_ACCT_TYPE_SMTP:
      service = "smtp";
      break;
    default:
      mutt_error (_("Unknown SASL profile"));
      return -1;
  }

  size = sizeof (local);
  if (!getsockname (conn->fd, (struct sockaddr *)&local, &size)) {
    if (!iptostring((struct sockaddr *)&local, size, iplocalport,
		      IP_PORT_BUFLEN) != SASL_OK)
      plp = iplocalport;
    else
      dprint (2, (debugfile, "SASL failed to parse local IP address\n"));
  }
  else
    dprint (2, (debugfile, "SASL failed to get local IP address\n"));
  
  size = sizeof (remote);
  if (!getpeername (conn->fd, (struct sockaddr *)&remote, &size)){
    if (!iptostring((struct sockaddr *)&remote, size, ipremoteport,
		      IP_PORT_BUFLEN) != SASL_OK)
      prp = ipremoteport;
    else
      dprint (2, (debugfile, "SASL failed to parse remote IP address\n"));
  }
  else
    dprint (2, (debugfile, "SASL failed to get remote IP address\n"));

  dprint (2, (debugfile, "SASL local ip: %s, remote ip:%s\n", NONULL(plp),
	      NONULL(prp)));
  
  rc = sasl_client_new (service, conn->account.host, plp, prp,
    mutt_sasl_get_callbacks (&conn->account), 0, saslconn);

  if (rc != SASL_OK)
  {
    mutt_error (_("Error allocating SASL connection"));
    return -1;
  }

  memset (&secprops, 0, sizeof (secprops));
  /* Work around a casting bug in the SASL krb4 module */
  secprops.max_ssf = 0x7fff;
  secprops.maxbufsize = M_SASL_MAXBUF;
  if (sasl_setprop (*saslconn, SASL_SEC_PROPS, &secprops) != SASL_OK)
  {
    mutt_error (_("Error setting SASL security properties"));
    return -1;
  }

  if (conn->ssf)
  {
    /* I'm not sure this actually has an effect, at least with SASLv2 */
    dprint (2, (debugfile, "External SSF: %d\n", conn->ssf));
    if (sasl_setprop (*saslconn, SASL_SSF_EXTERNAL, &(conn->ssf)) != SASL_OK)
    {
      mutt_error (_("Error setting SASL external security strength"));
      return -1;
    }
  }
  if (conn->account.user[0])
  {
    dprint (2, (debugfile, "External authentication name: %s\n", conn->account.user));
    if (sasl_setprop (*saslconn, SASL_AUTH_EXTERNAL, conn->account.user) != SASL_OK)
    {
      mutt_error (_("Error setting SASL external user name"));
      return -1;
    }
  }

  return 0;
}
Exemplo n.º 21
0
svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn,
                                apr_pool_t *pool,
                                server_baton_t *b,
                                enum access_type required,
                                svn_boolean_t needs_username)
{
  sasl_conn_t *sasl_ctx;
  apr_pool_t *subpool;
  apr_status_t apr_err;
  const char *localaddrport = NULL, *remoteaddrport = NULL;
  const char *mechlist, *val;
  char hostname[APRMAXHOSTLEN + 1];
  sasl_security_properties_t secprops;
  svn_boolean_t success, no_anonymous;
  int mech_count, result = SASL_OK;

  SVN_ERR(svn_ra_svn__get_addresses(&localaddrport, &remoteaddrport,
                                        conn, pool));
  apr_err = apr_gethostname(hostname, sizeof(hostname), pool);
  if (apr_err)
    {
      svn_error_t *err = svn_error_wrap_apr(apr_err, _("Can't get hostname"));
      SVN_ERR(write_failure(conn, pool, &err));
      return svn_ra_svn_flush(conn, pool);
    }

  /* Create a SASL context. SASL_SUCCESS_DATA tells SASL that the protocol
     supports sending data along with the final "success" message. */
  result = sasl_server_new(SVN_RA_SVN_SASL_NAME,
                           hostname, b->realm,
                           localaddrport, remoteaddrport,
                           NULL, SASL_SUCCESS_DATA,
                           &sasl_ctx);
  if (result != SASL_OK)
    {
      svn_error_t *err = svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                                          sasl_errstring(result, NULL, NULL));
      SVN_ERR(write_failure(conn, pool, &err));
      return svn_ra_svn_flush(conn, pool);
    }

  /* Make sure the context is always destroyed. */
  apr_pool_cleanup_register(b->pool, sasl_ctx, sasl_dispose_cb,
                            apr_pool_cleanup_null);

  /* Initialize security properties. */
  svn_ra_svn__default_secprops(&secprops);

  /* Don't allow ANONYMOUS if a username is required. */
  no_anonymous = needs_username || get_access(b, UNAUTHENTICATED) < required;
  if (no_anonymous)
    secprops.security_flags |= SASL_SEC_NOANONYMOUS;

  svn_config_get(b->cfg, &val,
                 SVN_CONFIG_SECTION_SASL,
                 SVN_CONFIG_OPTION_MIN_SSF,
                 "0");
  SVN_ERR(svn_cstring_atoui(&secprops.min_ssf, val));

  svn_config_get(b->cfg, &val,
                 SVN_CONFIG_SECTION_SASL,
                 SVN_CONFIG_OPTION_MAX_SSF,
                 "256");
  SVN_ERR(svn_cstring_atoui(&secprops.max_ssf, val));

  /* Set security properties. */
  result = sasl_setprop(sasl_ctx, SASL_SEC_PROPS, &secprops);
  if (result != SASL_OK)
    return fail_cmd(conn, pool, sasl_ctx);

  /* SASL needs to know if we are externally authenticated. */
  if (b->tunnel_user)
    result = sasl_setprop(sasl_ctx, SASL_AUTH_EXTERNAL, b->tunnel_user);
  if (result != SASL_OK)
    return fail_cmd(conn, pool, sasl_ctx);

  /* Get the list of mechanisms. */
  result = sasl_listmech(sasl_ctx, NULL, NULL, " ", NULL,
                         &mechlist, NULL, &mech_count);

  if (result != SASL_OK)
    return fail_cmd(conn, pool, sasl_ctx);

  if (mech_count == 0)
    {
      svn_error_t *err = svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                                          _("Could not obtain the list"
                                          " of SASL mechanisms"));
      SVN_ERR(write_failure(conn, pool, &err));
      return svn_ra_svn_flush(conn, pool);
    }

  /* Send the list of mechanisms and the realm to the client. */
  SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "(w)c",
                                        mechlist, b->realm));

  /* The main authentication loop. */
  subpool = svn_pool_create(pool);
  do
    {
      svn_pool_clear(subpool);
      SVN_ERR(try_auth(conn, sasl_ctx, subpool, b, &success));
    }
  while (!success);
  svn_pool_destroy(subpool);

  SVN_ERR(svn_ra_svn__enable_sasl_encryption(conn, sasl_ctx, pool));

  if (no_anonymous)
    {
      char *p;
      const void *user;

      /* Get the authenticated username. */
      result = sasl_getprop(sasl_ctx, SASL_USERNAME, &user);

      if (result != SASL_OK)
        return fail_cmd(conn, pool, sasl_ctx);

      if ((p = strchr(user, '@')) != NULL)
        {
          /* Drop the realm part. */
          b->user = apr_pstrndup(b->pool, user, p - (const char *)user);
        }
      else
        {
          svn_error_t *err;
          err = svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                                 _("Couldn't obtain the authenticated"
                                 " username"));
          SVN_ERR(write_failure(conn, pool, &err));
          return svn_ra_svn_flush(conn, pool);
        }
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 22
0
	POP3_SASL_STATE_E Pop3SaslWrapper::SaslInit(const Pop3SaslInfo_t& sasl_data)
	{
		if(sasl_data.user_name == "")
		{
			return POP3_SASL_ERROR;
		}
		
		this->sasl_data = sasl_data;

		//Initialize the callbacks
		/*sasl_callback[0].id  = SASL_CB_GETREALM;
		sasl_callback[0].proc = (int (*)())getrealm_func;
		sasl_callback[0].context = this;*/

		sasl_callback[0].id  = SASL_CB_USER;
		sasl_callback[0].proc = (int (*)())getauthname_func;
		sasl_callback[0].context = this;
		
		sasl_callback[1].id  = SASL_CB_AUTHNAME;
		sasl_callback[1].proc = (int (*)())getauthname_func;
		sasl_callback[1].context = this;

		sasl_callback[2].id  = SASL_CB_PASS;
		sasl_callback[2].proc = (int (*)())getsecret_func;
		sasl_callback[2].context = this;

		sasl_callback[3].id =  SASL_CB_CANON_USER;
		sasl_callback[3].proc = (int (*)())getcannonical_user_func;
		sasl_callback[3].context = this;

		sasl_callback[4].id  = SASL_CB_LIST_END;
		sasl_callback[4].proc = 0;
		sasl_callback[4].context = this;
	
		//Initialize the secret
		//If the allocated memory does not include length of password, the program results in memory corruption
		secret = (sasl_secret_t*) malloc(sizeof(sasl_secret_t)+sasl_data.user_pass.length());
		if(secret == 0)
		{
			//Error
			POP3_SASL_DEBUG("SASL-Error: Memory allocation failed\n");
			return POP3_SASL_ERROR;
		}

		secret->len = sasl_data.user_pass.length();
		memcpy(secret->data, sasl_data.user_pass.c_str(), secret->len);

		int sasl_result = sasl_client_init(0);

		if(sasl_result != SASL_OK)
		{
			//Error
			POP3_SASL_DEBUG("SASL-Error 2\n");
			return POP3_SASL_ERROR;
		}

		sasl_result = sasl_client_new(sasl_data.service.c_str(), sasl_data.hostname.c_str(), NULL, NULL, sasl_callback, 0, &conn);
		if(sasl_result != SASL_OK)
		{
			//Error
			POP3_SASL_DEBUG("SASL-Error 3\n");
			return POP3_SASL_ERROR;
		}

		memset(&secprops, 0, sizeof(secprops));
		secprops.maxbufsize = 8192; //TODO: Needs change
		secprops.max_ssf = UINT_MAX;
		sasl_result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);

		if(sasl_result != SASL_OK)
		{
			POP3_SASL_DEBUG("sasl_setprop failed\n");
			return POP3_SASL_ERROR;
		}

		return POP3_SASL_OK;
	}
Exemplo n.º 23
0
bool reds_sasl_start_auth(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
{
    const char *mechlist = NULL;
    sasl_security_properties_t secprops;
    int err;
    char *localAddr, *remoteAddr;
    int mechlistlen;
    RedsSASL *sasl = &stream->priv->sasl;

    if (!(localAddr = reds_stream_get_local_address(stream))) {
        goto error;
    }

    if (!(remoteAddr = reds_stream_get_remote_address(stream))) {
        free(localAddr);
        goto error;
    }

    err = sasl_server_new("spice",
                          NULL, /* FQDN - just delegates to gethostname */
                          NULL, /* User realm */
                          localAddr,
                          remoteAddr,
                          NULL, /* Callbacks, not needed */
                          SASL_SUCCESS_DATA,
                          &sasl->conn);
    free(localAddr);
    free(remoteAddr);
    localAddr = remoteAddr = NULL;

    if (err != SASL_OK) {
        spice_warning("sasl context setup failed %d (%s)",
                    err, sasl_errstring(err, NULL, NULL));
        sasl->conn = NULL;
        goto error;
    }

    /* Inform SASL that we've got an external SSF layer from TLS */
    if (stream->priv->ssl) {
        sasl_ssf_t ssf;

        ssf = SSL_get_cipher_bits(stream->priv->ssl, NULL);
        err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
        if (err != SASL_OK) {
            spice_warning("cannot set SASL external SSF %d (%s)",
                        err, sasl_errstring(err, NULL, NULL));
            goto error_dispose;
        }
    } else {
        sasl->wantSSF = 1;
    }

    memset(&secprops, 0, sizeof secprops);
    /* Inform SASL that we've got an external SSF layer from TLS */
    if (stream->priv->ssl) {
        /* If we've got TLS (or UNIX domain sock), we don't care about SSF */
        secprops.min_ssf = 0;
        secprops.max_ssf = 0;
        secprops.maxbufsize = 8192;
        secprops.security_flags = 0;
    } else {
        /* Plain TCP, better get an SSF layer */
        secprops.min_ssf = 56; /* Good enough to require kerberos */
        secprops.max_ssf = 100000; /* Arbitrary big number */
        secprops.maxbufsize = 8192;
        /* Forbid any anonymous or trivially crackable auth */
        secprops.security_flags =
            SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
    }

    err = sasl_setprop(sasl->conn, SASL_SEC_PROPS, &secprops);
    if (err != SASL_OK) {
        spice_warning("cannot set SASL security props %d (%s)",
                      err, sasl_errstring(err, NULL, NULL));
        goto error_dispose;
    }

    err = sasl_listmech(sasl->conn,
                        NULL, /* Don't need to set user */
                        "", /* Prefix */
                        ",", /* Separator */
                        "", /* Suffix */
                        &mechlist,
                        NULL,
                        NULL);
    if (err != SASL_OK || mechlist == NULL) {
        spice_warning("cannot list SASL mechanisms %d (%s)",
                      err, sasl_errdetail(sasl->conn));
        goto error_dispose;
    }

    spice_debug("Available mechanisms for client: '%s'", mechlist);

    sasl->mechlist = spice_strdup(mechlist);

    mechlistlen = strlen(mechlist);
    if (!reds_stream_write_all(stream, &mechlistlen, sizeof(uint32_t))
        || !reds_stream_write_all(stream, sasl->mechlist, mechlistlen)) {
        spice_warning("SASL mechanisms write error");
        goto error;
    }

    spice_debug("Wait for client mechname length");
    reds_stream_async_read(stream, (uint8_t *)&sasl->len, sizeof(uint32_t),
                           read_cb, opaque);

    return true;

error_dispose:
    sasl_dispose(&sasl->conn);
    sasl->conn = NULL;
error:
    return false;
}
Exemplo n.º 24
0
void start_auth_sasl(VncState *vs)
{
    const char *mechlist = NULL;
    sasl_security_properties_t secprops;
    int err;
    char *localAddr, *remoteAddr;
    int mechlistlen;

    VNC_DEBUG("Initialize SASL auth %p\n", vs->ioc);

    /* Get local & remote client addresses in form  IPADDR;PORT */
    localAddr = vnc_socket_ip_addr_string(vs->sioc, true, NULL);
    if (!localAddr) {
        goto authabort;
    }

    remoteAddr = vnc_socket_ip_addr_string(vs->sioc, false, NULL);
    if (!remoteAddr) {
        g_free(localAddr);
        goto authabort;
    }

    err = sasl_server_new("vnc",
                          NULL, /* FQDN - just delegates to gethostname */
                          NULL, /* User realm */
                          localAddr,
                          remoteAddr,
                          NULL, /* Callbacks, not needed */
                          SASL_SUCCESS_DATA,
                          &vs->sasl.conn);
    g_free(localAddr);
    g_free(remoteAddr);
    localAddr = remoteAddr = NULL;

    if (err != SASL_OK) {
        VNC_DEBUG("sasl context setup failed %d (%s)",
                  err, sasl_errstring(err, NULL, NULL));
        vs->sasl.conn = NULL;
        goto authabort;
    }

    /* Inform SASL that we've got an external SSF layer from TLS/x509 */
    if (vs->auth == VNC_AUTH_VENCRYPT &&
        vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
        Error *local_err = NULL;
        int keysize;
        sasl_ssf_t ssf;

        keysize = qcrypto_tls_session_get_key_size(vs->tls,
                                                   &local_err);
        if (keysize < 0) {
            VNC_DEBUG("cannot TLS get cipher size: %s\n",
                      error_get_pretty(local_err));
            error_free(local_err);
            sasl_dispose(&vs->sasl.conn);
            vs->sasl.conn = NULL;
            goto authabort;
        }
        ssf = keysize * CHAR_BIT; /* tls key size is bytes, sasl wants bits */

        err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
        if (err != SASL_OK) {
            VNC_DEBUG("cannot set SASL external SSF %d (%s)\n",
                      err, sasl_errstring(err, NULL, NULL));
            sasl_dispose(&vs->sasl.conn);
            vs->sasl.conn = NULL;
            goto authabort;
        }
    } else {
        vs->sasl.wantSSF = 1;
    }

    memset (&secprops, 0, sizeof secprops);
    /* Inform SASL that we've got an external SSF layer from TLS.
     *
     * Disable SSF, if using TLS+x509+SASL only. TLS without x509
     * is not sufficiently strong
     */
    if (vs->vd->is_unix ||
        (vs->auth == VNC_AUTH_VENCRYPT &&
         vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) {
        /* If we've got TLS or UNIX domain sock, we don't care about SSF */
        secprops.min_ssf = 0;
        secprops.max_ssf = 0;
        secprops.maxbufsize = 8192;
        secprops.security_flags = 0;
    } else {
        /* Plain TCP, better get an SSF layer */
        secprops.min_ssf = 56; /* Good enough to require kerberos */
        secprops.max_ssf = 100000; /* Arbitrary big number */
        secprops.maxbufsize = 8192;
        /* Forbid any anonymous or trivially crackable auth */
        secprops.security_flags =
            SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
    }

    err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops);
    if (err != SASL_OK) {
        VNC_DEBUG("cannot set SASL security props %d (%s)\n",
                  err, sasl_errstring(err, NULL, NULL));
        sasl_dispose(&vs->sasl.conn);
        vs->sasl.conn = NULL;
        goto authabort;
    }

    err = sasl_listmech(vs->sasl.conn,
                        NULL, /* Don't need to set user */
                        "", /* Prefix */
                        ",", /* Separator */
                        "", /* Suffix */
                        &mechlist,
                        NULL,
                        NULL);
    if (err != SASL_OK) {
        VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n",
                  err, sasl_errdetail(vs->sasl.conn));
        sasl_dispose(&vs->sasl.conn);
        vs->sasl.conn = NULL;
        goto authabort;
    }
    VNC_DEBUG("Available mechanisms for client: '%s'\n", mechlist);

    vs->sasl.mechlist = g_strdup(mechlist);
    mechlistlen = strlen(mechlist);
    vnc_write_u32(vs, mechlistlen);
    vnc_write(vs, mechlist, mechlistlen);
    vnc_flush(vs);

    VNC_DEBUG("Wait for client mechname length\n");
    vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4);

    return;

 authabort:
    vnc_client_error(vs);
}
Exemplo n.º 25
0
static int login(struct backend *s, const char *userid,
                 sasl_callback_t *cb, const char **status,
                 int noauth __attribute__((unused)))
{
    int r = 0;
    socklen_t addrsize;
    struct sockaddr_storage saddr_l, saddr_r;
    char remoteip[60], localip[60];
    static struct buf buf = BUF_INITIALIZER;
    sasl_security_properties_t secprops =
        { 0, 0xFF, PROT_BUFSIZE, 0, NULL, NULL }; /* default secprops */
    const char *mech_conf, *pass, *clientout = NULL;
    struct auth_scheme_t *scheme = NULL;
    unsigned need_tls = 0, tls_done = 0, auth_done = 0, clientoutlen;
    hdrcache_t hdrs = NULL;

    if (status) *status = NULL;

    /* set the IP addresses */
    addrsize = sizeof(struct sockaddr_storage);
    if (getpeername(s->sock, (struct sockaddr *) &saddr_r, &addrsize) ||
        iptostring((struct sockaddr *) &saddr_r, addrsize, remoteip, 60)) {
        if (status) *status = "Failed to get remote IP address";
        return SASL_FAIL;
    }

    addrsize = sizeof(struct sockaddr_storage);
    if (getsockname(s->sock, (struct sockaddr *) &saddr_l, &addrsize) ||
        iptostring((struct sockaddr *) &saddr_l, addrsize, localip, 60)) {
        if (status) *status = "Failed to get local IP address";
        return SASL_FAIL;
    }

    /* Create callbacks, if necessary */
    if (!cb) {
        buf_setmap(&buf, s->hostname, strcspn(s->hostname, "."));
        buf_appendcstr(&buf, "_password");
        pass = config_getoverflowstring(buf_cstring(&buf), NULL);
        if (!pass) pass = config_getstring(IMAPOPT_PROXY_PASSWORD);
        cb = mysasl_callbacks(NULL, /* userid */
                              config_getstring(IMAPOPT_PROXY_AUTHNAME),
                              config_getstring(IMAPOPT_PROXY_REALM),
                              pass);
        s->sasl_cb = cb;
    }

    /* Create SASL context */
    r = sasl_client_new(s->prot->sasl_service, s->hostname,
                        localip, remoteip, cb, SASL_USAGE_FLAGS, &s->saslconn);
    if (r != SASL_OK) goto done;

    r = sasl_setprop(s->saslconn, SASL_SEC_PROPS, &secprops);
    if (r != SASL_OK) goto done;

    /* Get SASL mechanism list.  We can force a particular
       mechanism using a <shorthost>_mechs option */
    buf_setmap(&buf, s->hostname, strcspn(s->hostname, "."));
    buf_appendcstr(&buf, "_mechs");
    if (!(mech_conf = config_getoverflowstring(buf_cstring(&buf), NULL))) {
        mech_conf = config_getstring(IMAPOPT_FORCE_SASL_CLIENT_MECH);
    }

    do {
        unsigned code;
        const char **hdr, *errstr, *serverin;
        char base64[BASE64_BUF_SIZE+1];
        unsigned int serverinlen;
        struct body_t resp_body;
#ifdef SASL_HTTP_REQUEST
        sasl_http_request_t httpreq = { "OPTIONS",      /* Method */
                                        "*",            /* URI */
                                        (u_char *) "",  /* Empty body */
                                        0,              /* Zero-length body */
                                        0 };            /* Persistent cxn? */
#endif

        /* Base64 encode any client response, if necessary */
        if (clientout && scheme && (scheme->flags & AUTH_BASE64)) {
            r = sasl_encode64(clientout, clientoutlen,
                              base64, BASE64_BUF_SIZE, &clientoutlen);
            if (r != SASL_OK) break;

            clientout = base64;
        }

        /* Send Authorization and/or Upgrade request to server */
        prot_puts(s->out, "OPTIONS * HTTP/1.1\r\n");
        prot_printf(s->out, "Host: %s\r\n", s->hostname);
        prot_printf(s->out, "User-Agent: %s\r\n", buf_cstring(&serverinfo));
        if (scheme) {
            prot_printf(s->out, "Authorization: %s %s\r\n",
                        scheme->name, clientout ? clientout : "");
            prot_printf(s->out, "Authorize-As: %s\r\n",
                        userid ? userid : "anonymous");
        }
        else {
            prot_printf(s->out, "Upgrade: %s\r\n", TLS_VERSION);
            if (need_tls) {
                prot_puts(s->out, "Connection: Upgrade\r\n");
                need_tls = 0;
            }
            prot_puts(s->out, "Authorization: \r\n");
        }
        prot_puts(s->out, "\r\n");
        prot_flush(s->out);

        serverin = clientout = NULL;
        serverinlen = clientoutlen = 0;

        /* Read response(s) from backend until final response or error */
        do {
            resp_body.flags = BODY_DISCARD;
            r = http_read_response(s, METH_OPTIONS, &code, NULL,
                                   &hdrs, &resp_body, &errstr);
            if (r) {
                if (status) *status = errstr;
                break;
            }

            if (code == 101) {  /* Switching Protocols */
                if (tls_done) {
                    r = HTTP_BAD_GATEWAY;
                    if (status) *status = "TLS already active";
                    break;
                }
                else if (backend_starttls(s, NULL, NULL, NULL)) {
                    r = HTTP_SERVER_ERROR;
                    if (status) *status = "Unable to start TLS";
                    break;
                }
                else tls_done = 1;
            }
        } while (code < 200);

        switch (code) {
        default: /* Failure */
            if (!r) {
                r = HTTP_BAD_GATEWAY;
                if (status) {
                    buf_reset(&buf);
                    buf_printf(&buf,
                               "Unexpected status code from backend: %u", code);
                    *status = buf_cstring(&buf);
                }
            }
            break;

        case 426: /* Upgrade Required */
            if (tls_done) {
                r = HTTP_BAD_GATEWAY;
                if (status) *status = "TLS already active";
            }
            else need_tls = 1;
            break;

        case 200: /* OK */
            if (scheme->recv_success &&
                (serverin = scheme->recv_success(hdrs))) {
                /* Process success data */
                serverinlen = strlen(serverin);
                goto challenge;
            }
            break;

        case 401: /* Unauthorized */
            if (auth_done) {
                r = SASL_BADAUTH;
                break;
            }

            if (!serverin) {
                int i = 0;

                hdr = spool_getheader(hdrs, "WWW-Authenticate");

                if (!scheme) {
                    unsigned avail_auth_schemes = 0;
                    const char *mech = NULL;
                    size_t len;

                    /* Compare authentication schemes offered in
                     * WWW-Authenticate header(s) to what we support */
                    buf_reset(&buf);
                    for (i = 0; hdr && hdr[i]; i++) {
                        len = strcspn(hdr[i], " ");

                        for (scheme = auth_schemes; scheme->name; scheme++) {
                            if (!strncmp(scheme->name, hdr[i], len) &&
                                !((scheme->flags & AUTH_NEED_PERSIST) &&
                                  (resp_body.flags & BODY_CLOSE))) {
                                /* Tag the scheme as available */
                                avail_auth_schemes |= (1 << scheme->idx);

                                /* Add SASL-based schemes to SASL mech list */
                                if (scheme->saslmech) {
                                    if (buf_len(&buf)) buf_putc(&buf, ' ');
                                    buf_appendcstr(&buf, scheme->saslmech);
                                }
                                break;
                            }
                        }
                    }

                    /* If we have a mech_conf, use it */
                    if (mech_conf && buf_len(&buf)) {
                        char *conf = xstrdup(mech_conf);
                        char *newmechlist =
                            intersect_mechlists(conf,
                                                (char *) buf_cstring(&buf));

                        if (newmechlist) {
                            buf_setcstr(&buf, newmechlist);
                            free(newmechlist);
                        }
                        else {
                            syslog(LOG_DEBUG, "%s did not offer %s",
                                   s->hostname, mech_conf);
                            buf_reset(&buf);
                        }
                        free(conf);
                    }

#ifdef SASL_HTTP_REQUEST
                    /* Set HTTP request as specified above (REQUIRED) */
                    httpreq.non_persist = (resp_body.flags & BODY_CLOSE);
                    sasl_setprop(s->saslconn, SASL_HTTP_REQUEST, &httpreq);
#endif

                    /* Try to start SASL exchange using available mechs */
                    r = sasl_client_start(s->saslconn, buf_cstring(&buf),
                                          NULL,         /* no prompts */
                                          NULL, NULL,   /* no initial resp */
                                          &mech);

                    if (mech) {
                        /* Find auth scheme associated with chosen SASL mech */
                        for (scheme = auth_schemes; scheme->name; scheme++) {
                            if (scheme->saslmech &&
                                !strcmp(scheme->saslmech, mech)) break;
                        }
                    }
                    else {
                        /* No matching SASL mechs - try Basic */
                        scheme = &auth_schemes[AUTH_BASIC];
                        if (!(avail_auth_schemes & (1 << scheme->idx))) {
                            need_tls = !tls_done;
                            break;  /* case 401 */
                        }
                    }

                    /* Find the associated WWW-Authenticate header */
                    for (i = 0; hdr && hdr[i]; i++) {
                        len = strcspn(hdr[i], " ");
                        if (!strncmp(scheme->name, hdr[i], len)) break;
                    }
                }

                /* Get server challenge, if any */
                if (hdr) {
                    const char *p = strchr(hdr[i], ' ');
                    serverin = p ? ++p : "";
                    serverinlen = strlen(serverin);
                }
            }

        challenge:
            if (serverin) {
                /* Perform the next step in the auth exchange */

                if (scheme->idx == AUTH_BASIC) {
                    /* Don't care about "realm" in server challenge */
                    const char *authid =
                        callback_getdata(s->saslconn, cb, SASL_CB_AUTHNAME);
                    pass = callback_getdata(s->saslconn, cb, SASL_CB_PASS);

                    buf_reset(&buf);
                    buf_printf(&buf, "%s:%s", authid, pass);
                    clientout = buf_cstring(&buf);
                    clientoutlen = buf_len(&buf);
                    auth_done = 1;
                }
                else {
                    /* Base64 decode any server challenge, if necessary */
                    if (serverin && (scheme->flags & AUTH_BASE64)) {
                        r = sasl_decode64(serverin, serverinlen,
                                          base64, BASE64_BUF_SIZE, &serverinlen);
                        if (r != SASL_OK) break;  /* case 401 */

                        serverin = base64;
                    }

                    /* SASL mech (Digest, Negotiate, NTLM) */
                    r = sasl_client_step(s->saslconn, serverin, serverinlen,
                                         NULL,          /* no prompts */
                                         &clientout, &clientoutlen);
                    if (r == SASL_OK) auth_done = 1;
                }
            }
            break;  /* case 401 */
        }

    } while (need_tls || clientout);

  done:
    if (hdrs) spool_free_hdrcache(hdrs);

    if (r && status && !*status) *status = sasl_errstring(r, NULL, NULL);

    return r;
}