Example #1
0
  void step(const std::string& data)
  {
    if (status != STEPPING) {
      AuthenticationErrorMessage message;
      message.set_error("Unexpected authentication 'step' received");
      send(pid, message);
      status = ERROR;
      promise.fail(message.error());
      return;
    }

    LOG(INFO) << "Received SASL authentication step";

    const char* output = NULL;
    unsigned length = 0;

    int result = sasl_server_step(
        connection,
        data.length() == 0 ? NULL : data.data(),
        data.length(),
        &output,
        &length);

    handle(result, output, length);
  }
Example #2
0
/* (err, data) = cyrussasl.server_step(conn, data)
 *
 * Arguments:
 *   conn: the return result from a previous call to server_new
 *   data: any data that the client might have sent from the previous step.
 *         Note that data may still be an empty string or nil. (Like the 
 *         argument of the same name to server_start.)
 *
 * Return values:
 *   err: the (integer) SASL error code reflecting the state of the attempt 
 *        (e.g. SASL_OK, SASL_CONTINUE, SASL_BADMECH, ...)
 *   data: data that the server wants to send to the client in order 
 *         to continue the authN attempt. Returned as a Lua string object.
 */
static int cyrussasl_sasl_server_step(lua_State *l)
{
  int numargs = lua_gettop(l);
  int err;
  struct _sasl_ctx *ctx = NULL;
  const char *data = NULL;
  size_t len;
  unsigned outlen;

  if (numargs != 2) {
    lua_pushstring(l, 
		   "usage: (err, data) = cyrussasl.server_step(conn, data)");
    lua_error(l);
    return 0;
  }

  ctx = get_context(l, 1);
  data = tolstring(l, 2, &len);

  err = sasl_server_step( ctx->conn,
			  data,
			  len,
			  &data,
			  &outlen );

  /* Form the reply and push onto the stack */
  lua_pushinteger(l, err);          /* SASL_CONTINUE, SASL_OK, et al  */
  if (data) {
    lua_pushlstring(l, data, outlen); /* server's reply to the client   */
  } else {
    lua_pushnil(l);
  }
  return 2;                         /* returning 2 items on Lua stack */
}
Example #3
0
static int pni_wrap_server_step(pni_sasl_t *sasl, const pn_bytes_t *in)
{
    int result;
    const char *out;
    unsigned outlen;
    sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context;
    result = sasl_server_step(cyrus_conn,
                              in->start, in->size,
                              &out, &outlen);

    sasl->bytes_out.start = out;
    sasl->bytes_out.size = outlen;
    return result;
}
Example #4
0
int virNetSASLSessionServerStep(virNetSASLSessionPtr sasl,
                                const char *clientin,
                                size_t clientinlen,
                                const char **serverout,
                                size_t *serveroutlen)
{
    unsigned inlen = clientinlen;
    unsigned outlen = 0;
    int err;
    int ret = -1;

    virObjectLock(sasl);
    err = sasl_server_step(sasl->conn,
                           clientin,
                           inlen,
                           serverout,
                           &outlen);

    *serveroutlen = outlen;

    switch (err) {
    case SASL_OK:
        if (virNetSASLSessionUpdateBufSize(sasl) < 0)
            goto cleanup;
        ret = VIR_NET_SASL_COMPLETE;
        break;
    case SASL_CONTINUE:
        ret = VIR_NET_SASL_CONTINUE;
        break;
    case SASL_INTERACT:
        ret = VIR_NET_SASL_INTERACT;
        break;
    default:
        virReportError(VIR_ERR_AUTH_FAILED,
                       _("Failed to start SASL negotiation: %d (%s)"),
                       err, sasl_errdetail(sasl->conn));
        break;
    }

cleanup:
    virObjectUnlock(sasl);
    return ret;
}
Example #5
0
uint8_t* TSaslServer::evaluateChallengeOrResponse(const uint8_t* response,
        const uint32_t len, uint32_t* resLen) {
    uint8_t* out = NULL;
    uint32_t outlen = 0;
    uint32_t result;

    if (!serverStarted) {
        result = sasl_server_start(conn,
                                   (const char *)response, NULL, 0, (const char **)&out, &outlen);
    } else {
        result = sasl_server_step(conn,
                                  (const char*)response, len, (const char**)&out, &outlen);
    }

    if (result == SASL_OK) {
        authComplete = true;
    } else if (result != SASL_CONTINUE) {
        throw SaslServerImplException(sasl_errdetail(conn));
    }
    serverStarted = true;

    *resLen = outlen;
    return out;
}
Example #6
0
static int
__pmAuthServerNegotiation(int fd, int ssf, __pmHashCtl *attrs)
{
    int sts, saslsts;
    int pinned, length, count;
    char *payload, *offset;
    sasl_conn_t *sasl_conn;
    __pmPDU *pb;

    if (pmDebugOptions.auth)
	fprintf(stderr, "__pmAuthServerNegotiation(fd=%d, ssf=%d)\n",
		fd, ssf);

    if ((sasl_conn = (sasl_conn_t *)__pmGetUserAuthData(fd)) == NULL)
        return -EINVAL;

    /* setup all the security properties for this connection */
    if ((sts = __pmAuthServerSetProperties(sasl_conn, ssf)) < 0)
	return sts;

    saslsts = sasl_listmech(sasl_conn,
			    NULL, NULL, " ", NULL,
                            (const char **)&payload,
                            (unsigned int *)&length,
                            &count);
    if (saslsts != SASL_OK && saslsts != SASL_CONTINUE) {
	pmNotifyErr(LOG_ERR, "Generating client mechanism list: %s",
			sasl_errstring(saslsts, NULL, NULL));
	return __pmSecureSocketsError(saslsts);
    }
    if (pmDebugOptions.auth)
	fprintf(stderr, "__pmAuthServerNegotiation - sending mechanism list "
		"(%d items, %d bytes): \"%s\"\n", count, length, payload);

    if ((sts = __pmSendAuth(fd, FROM_ANON, 0, payload, length)) < 0)
	return sts;

    if (pmDebugOptions.auth)
	fprintf(stderr, "__pmAuthServerNegotiation - wait for mechanism\n");

    sts = pinned = __pmGetPDU(fd, ANY_SIZE, TIMEOUT_DEFAULT, &pb);
    if (sts == PDU_AUTH) {
        sts = __pmDecodeAuth(pb, &count, &payload, &length);
        if (sts >= 0) {
	    for (count = 0; count < length; count++) {
		if (payload[count] == '\0')
		    break;
	    }
	    if (count < length)	{  /* found an initial response */
		length = length - count - 1;
		offset = payload + count + 1;
	    } else {
		length = 0;
		offset = NULL;
	    }

	    saslsts = sasl_server_start(sasl_conn, payload,
				offset, length,
				(const char **)&payload,
				(unsigned int *)&length);
	    if (saslsts != SASL_OK && saslsts != SASL_CONTINUE) {
		sts = __pmSecureSocketsError(saslsts);
		if (pmDebugOptions.auth)
		    fprintf(stderr, "sasl_server_start failed: %d (%s)\n",
				    saslsts, pmErrStr(sts));
	    } else {
		if (pmDebugOptions.auth)
		    fprintf(stderr, "sasl_server_start success: sts=%s\n",
			    saslsts == SASL_CONTINUE ? "continue" : "ok");
	    }
	}
    }
    else if (sts == PDU_ERROR)
	__pmDecodeError(pb, &sts);
    else if (sts != PM_ERR_TIMEOUT)
	sts = PM_ERR_IPC;

    if (pinned > 0)
	__pmUnpinPDUBuf(pb);
    if (sts < 0)
	return sts;

    if (pmDebugOptions.auth)
	fprintf(stderr, "__pmAuthServerNegotiation method negotiated\n");

    while (saslsts == SASL_CONTINUE) {
	if (!payload) {
	    pmNotifyErr(LOG_ERR, "No SASL data to send");
	    sts = -EINVAL;
	    break;
	}
	if ((sts = __pmSendAuth(fd, FROM_ANON, 0, payload, length)) < 0)
	    break;

	if (pmDebugOptions.auth)
	    fprintf(stderr, "__pmAuthServerNegotiation awaiting response\n");

	sts = pinned = __pmGetPDU(fd, ANY_SIZE, TIMEOUT_DEFAULT, &pb);
	if (sts == PDU_AUTH) {
	    sts = __pmDecodeAuth(pb, &count, &payload, &length);
	    if (sts >= 0) {
		sts = saslsts = sasl_server_step(sasl_conn, payload, length,
                                                 (const char **)&payload,
                                                 (unsigned int *)&length);
		if (sts != SASL_OK && sts != SASL_CONTINUE) {
		    sts = __pmSecureSocketsError(sts);
		    break;
		}
		if (pmDebugOptions.auth) {
		    fprintf(stderr, "__pmAuthServerNegotiation"
				    " step recv (%d bytes)\n", length);
		}
	    }
	}
	else if (sts == PDU_ERROR)
	    __pmDecodeError(pb, &sts);
	else if (sts != PM_ERR_TIMEOUT)
	    sts = PM_ERR_IPC;

	if (pinned > 0)
	    __pmUnpinPDUBuf(pb);
	if (sts < 0)
	    break;
    }

    if (sts < 0) {
	if (pmDebugOptions.auth)
	    fprintf(stderr, "__pmAuthServerNegotiation loop failed: %d\n", sts);
	return sts;
    }

    return __pmAuthServerSetAttributes(sasl_conn, attrs);
}
Example #7
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 */
}
Example #8
0
/* NOTE: success_data will need to be free()d by the caller */
int saslserver(sasl_conn_t *conn, const char *mech,
	       const char *init_resp, const char *resp_prefix,
	       const char *continuation, const char *empty_chal,
               struct protstream *pin, struct protstream *pout,
	       int *sasl_result, char **success_data)
{
    char base64[BASE64_BUF_SIZE+1];
    char *clientin = NULL;
    unsigned int clientinlen = 0;
    const char *serverout = NULL;
    unsigned int serveroutlen = 0;
    int r = SASL_OK;

    if (success_data) *success_data = NULL;

    /* initial response */
    if (init_resp) {
	clientin = base64;
	if (!strcmp(init_resp, "=")) {
	    /* zero-length initial response */
	    base64[0] = '\0';
	}
	else {
	    r = sasl_decode64(init_resp, strlen(init_resp),
			      clientin, BASE64_BUF_SIZE, &clientinlen);
	}
    }

    /* start the exchange */
    if (r == SASL_OK)
	r = sasl_server_start(conn, mech, clientin, clientinlen,
			      &serverout, &serveroutlen);

    while (r == SASL_CONTINUE) {
	char *p;

	/* send the challenge to the client */
	if (serveroutlen) {
	    r = sasl_encode64(serverout, serveroutlen,
			      base64, BASE64_BUF_SIZE, NULL);
	    if (r != SASL_OK) break;
	    serverout = base64;
	}
	else {
	    serverout = empty_chal;
	}

	prot_printf(pout, "%s%s\r\n", continuation, serverout);
	prot_flush(pout);

	/* get response from the client */
	if (!prot_fgets(base64, BASE64_BUF_SIZE, pin) ||
	    strncasecmp(base64, resp_prefix, strlen(resp_prefix))) {
	    if (sasl_result) *sasl_result = SASL_FAIL;
	    return IMAP_SASL_PROTERR;
	}

	/* trim CRLF */
	p = base64 + strlen(base64) - 1;
	if (p >= base64 && *p == '\n') *p-- = '\0';
	if (p >= base64 && *p == '\r') *p-- = '\0';

	/* trim prefix */
	p = base64 + strlen(resp_prefix);

	/* check if client cancelled */
	if (p[0] == '*') {
	    if(sasl_result) *sasl_result = SASL_BADPROT;
	    return IMAP_SASL_CANCEL;
	}

	/* decode the response */
	clientin = base64;
	r = sasl_decode64(p, strlen(p),
			  clientin, BASE64_BUF_SIZE, &clientinlen);
	if (r != SASL_OK) break;

	/* do the next step */
	r = sasl_server_step(conn, clientin, clientinlen,
			     &serverout, &serveroutlen);
    }

    /* success data */
    if (r == SASL_OK && serverout && success_data) {
	r = sasl_encode64(serverout, serveroutlen,
			  base64, BASE64_BUF_SIZE, NULL);
	if (r == SASL_OK)
	    *success_data = (char *) xstrdup(base64);
    }

    if (sasl_result) *sasl_result = r;
    return (r == SASL_OK ? 0 : IMAP_SASL_FAIL);
}
Example #9
0
/* do the sasl negotiation; return -1 if it fails */
int mysasl_negotiate(FILE *in, FILE *out, sasl_conn_t *conn)
{
    char buf[8192];
    char chosenmech[128];
    const char *data;
    int len;
    int r = SASL_FAIL;
    const char *userid;
#ifdef HAVE_GSS_GET_NAME_ATTRIBUTE
    gss_name_t peer = GSS_C_NO_NAME;
#endif
    
    /* generate the capability list */
    if (mech) {
	dprintf(2, "forcing use of mechanism %s\n", mech);
	data = strdup(mech);
	len = strlen(data);
    } else {
	int count;

	dprintf(1, "generating client mechanism list... ");
	r = sasl_listmech(conn, NULL, NULL, " ", NULL,
			  &data, (unsigned int *)&len, &count);
	if (r != SASL_OK) saslfail(r, "generating mechanism list");
	dprintf(1, "%d mechanisms\n", count);
    }

    /* send capability list to client */
    send_string(out, data, len);

    dprintf(1, "waiting for client mechanism...\n");
    len = recv_string(in, chosenmech, sizeof chosenmech);
    if (len <= 0) {
	printf("client didn't choose mechanism\n");
	fputc('N', out); /* send NO to client */
	fflush(out);
	return -1;
    }

    if (mech && strcasecmp(mech, chosenmech)) {
	printf("client didn't choose mandatory mechanism\n");
	fputc('N', out); /* send NO to client */
	fflush(out);
	return -1;
    }

    len = recv_string(in, buf, sizeof(buf));
    if(len != 1) {
	saslerr(r, "didn't receive first-send parameter correctly");
	fprintf(stderr, "%s\n", sasl_errdetail(conn));
	fputc('N', out);
	fflush(out);
	return -1;
    }

    if(buf[0] == 'Y') {
        /* receive initial response (if any) */
        len = recv_string(in, buf, sizeof(buf));

        /* start libsasl negotiation */
        r = sasl_server_start(conn, chosenmech, buf, len,
			      &data, (unsigned int *)&len);
    } else {
	r = sasl_server_start(conn, chosenmech, NULL, 0,
			      &data, (unsigned int *)&len);
    }
    
    if (r != SASL_OK && r != SASL_CONTINUE) {
	saslerr(r, "starting SASL negotiation");
	fprintf(stderr, "%s\n", sasl_errdetail(conn));
	fputc('N', out); /* send NO to client */
	fflush(out);
	return -1;
    }

    while (r == SASL_CONTINUE) {
	if (data) {
	    dprintf(2, "sending response length %d...\n", len);
	    fputc('C', out); /* send CONTINUE to client */
	    send_string(out, data, len);
	} else {
	    dprintf(2, "sending null response...\n");
	    fputc('C', out); /* send CONTINUE to client */
	    send_string(out, "", 0);
	}

	dprintf(1, "waiting for client reply...\n");
	len = recv_string(in, buf, sizeof buf);
	if (len < 0) {
	    printf("client disconnected\n");
	    return -1;
	}

	r = sasl_server_step(conn, buf, len, &data, (unsigned int *)&len);
	if (r != SASL_OK && r != SASL_CONTINUE) {
	    saslerr(r, "performing SASL negotiation");
	    fprintf(stderr, "%s\n", sasl_errdetail(conn));
	    fputc('N', out); /* send NO to client */
	    fflush(out);
	    return -1;
	}
    }

    if (r != SASL_OK) {
	saslerr(r, "incorrect authentication");
	fprintf(stderr, "%s\n", sasl_errdetail(conn));
	fputc('N', out); /* send NO to client */
	fflush(out);
	return -1;
    }

    fputc('O', out); /* send OK to client */
    fflush(out);
    dprintf(1, "negotiation complete\n");

    r = sasl_getprop(conn, SASL_USERNAME, (const void **) &userid);
    printf("successful authentication '%s'\n", userid);

#ifdef HAVE_GSS_GET_NAME_ATTRIBUTE
    r = sasl_getprop(conn, SASL_GSS_PEER_NAME, (const void **) &peer);
    if (peer != GSS_C_NO_NAME) {
        OM_uint32 minor;
        enumerateAttributes(&minor, peer, 1);
    }
#endif

    return 0;
}
Example #10
0
static svn_error_t *try_auth(svn_ra_svn_conn_t *conn,
                             sasl_conn_t *sasl_ctx,
                             apr_pool_t *pool,
                             server_baton_t *b,
                             svn_boolean_t *success)
{
  const char *out, *mech;
  const svn_string_t *arg = NULL, *in;
  unsigned int outlen;
  int result;
  svn_boolean_t use_base64;

  *success = FALSE;

  /* Read the client's chosen mech and the initial token. */
  SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "w(?s)", &mech, &in));

  if (strcmp(mech, "EXTERNAL") == 0 && !in)
    in = svn_string_create(b->tunnel_user, pool);
  else if (in)
    in = svn_base64_decode_string(in, pool);

  /* For CRAM-MD5, we don't base64-encode stuff. */
  use_base64 = (strcmp(mech, "CRAM-MD5") != 0);

  result = sasl_server_start(sasl_ctx, mech,
                             in ? in->data : NULL,
                             in ? in->len : 0, &out, &outlen);

  if (result != SASL_OK && result != SASL_CONTINUE)
    return fail_auth(conn, pool, sasl_ctx);

  while (result == SASL_CONTINUE)
    {
      svn_ra_svn_item_t *item;

      arg = svn_string_ncreate(out, outlen, pool);
      /* Encode what we send to the client. */
      if (use_base64)
        arg = svn_base64_encode_string2(arg, TRUE, pool);

      SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(s)", "step", arg));

      /* Read and decode the client response. */
      SVN_ERR(svn_ra_svn_read_item(conn, pool, &item));
      if (item->kind != SVN_RA_SVN_STRING)
        return SVN_NO_ERROR;

      in = item->u.string;
      if (use_base64)
        in = svn_base64_decode_string(in, pool);
      result = sasl_server_step(sasl_ctx, in->data, in->len, &out, &outlen);
    }

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

  /* Send our last response, if necessary. */
  if (outlen)
    arg = svn_base64_encode_string2(svn_string_ncreate(out, outlen, pool), TRUE,
                                    pool);
  else
    arg = NULL;

  *success = TRUE;
  SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(?s)", "success", arg));

  return SVN_NO_ERROR;
}
Example #11
0
RedsSaslError reds_sasl_handle_auth_step(RedsStream *stream, AsyncReadDone read_cb, void *opaque)
{
    const char *serverout;
    unsigned int serveroutlen;
    int err;
    char *clientdata = NULL;
    RedsSASL *sasl = &stream->priv->sasl;
    uint32_t datalen = sasl->len;

    /* NB, distinction of NULL vs "" is *critical* in SASL */
    if (datalen) {
        clientdata = sasl->data;
        clientdata[datalen - 1] = '\0'; /* Wire includes '\0', but make sure */
        datalen--; /* Don't count NULL byte when passing to _start() */
    }

    spice_debug("Step using SASL Data %p (%d bytes)",
               clientdata, datalen);
    err = sasl_server_step(sasl->conn,
                           clientdata,
                           datalen,
                           &serverout,
                           &serveroutlen);
    if (err != SASL_OK &&
        err != SASL_CONTINUE) {
        spice_warning("sasl step failed %d (%s)",
                      err, sasl_errdetail(sasl->conn));
        return REDS_SASL_ERROR_GENERIC;
    }

    if (serveroutlen > SASL_DATA_MAX_LEN) {
        spice_warning("sasl step reply data too long %d",
                      serveroutlen);
        return REDS_SASL_ERROR_INVALID_DATA;
    }

    spice_debug("SASL return data %d bytes, %p", serveroutlen, serverout);

    if (serveroutlen) {
        serveroutlen += 1;
        reds_stream_write_all(stream, &serveroutlen, sizeof(uint32_t));
        reds_stream_write_all(stream, serverout, serveroutlen);
    } else {
        reds_stream_write_all(stream, &serveroutlen, sizeof(uint32_t));
    }

    /* Whether auth is complete */
    reds_stream_write_u8(stream, err == SASL_CONTINUE ? 0 : 1);

    if (err == SASL_CONTINUE) {
        spice_debug("%s", "Authentication must continue (step)");
        /* Wait for step length */
        reds_stream_async_read(stream, (uint8_t *)&sasl->len, sizeof(uint32_t),
                               read_cb, opaque);
        return REDS_SASL_ERROR_CONTINUE;
    } else {
        int ssf;

        if (auth_sasl_check_ssf(sasl, &ssf) == 0) {
            spice_warning("Authentication rejected for weak SSF");
            goto authreject;
        }

        spice_debug("Authentication successful");
        reds_stream_write_u32(stream, SPICE_LINK_ERR_OK); /* Accept auth */

        /*
         * Delay writing in SSF encoded until now
         */
        sasl->runSSF = ssf;
        reds_stream_disable_writev(stream); /* make sure writev isn't called directly anymore */

        return REDS_SASL_ERROR_OK;
    }

authreject:
    reds_stream_write_u32(stream, 1); /* Reject auth */
    reds_stream_write_u32(stream, sizeof("Authentication failed"));
    reds_stream_write_all(stream, "Authentication failed", sizeof("Authentication failed"));

    return REDS_SASL_ERROR_AUTH_FAILED;
}
Example #12
0
	int serverTryAgain()
	{
		if(step == 0) {
			if(!ca_skip) {
				const char *clientin = 0;
				unsigned int clientinlen = 0;
				if(in_useClientInit) {
					clientin = in_clientInit.data();
					clientinlen = in_clientInit.size();
				}
				const char *serverout;
				unsigned int serveroutlen;
				ca_flag = false;
				int r = sasl_server_start(con, in_mech.toLatin1().data(), clientin, clientinlen, &serverout, &serveroutlen);
				if(r != SASL_OK && r != SASL_CONTINUE) {
					err = saslErrorCond(r);
					return Error;
				}
				out_buf = makeByteArray(serverout, serveroutlen);
				last_r = r;
				if(ca_flag && !ca_done) {
					ca_done = true;
					ca_skip = true;
					return AuthCheck;
				}
			}
			ca_skip = false;
			++step;

			if(last_r == SASL_OK) {
				getssfparams();
				return Success;
			}
			return Continue;
		}
		else {
			if(!ca_skip) {
				const char *serverout;
				unsigned int serveroutlen;
				int r = sasl_server_step(con, in_buf.data(), in_buf.size(), &serverout, &serveroutlen);
				if(r != SASL_OK && r != SASL_CONTINUE) {
					err = saslErrorCond(r);
					return Error;
				}
				if(r == SASL_OK)
					out_buf.resize(0);
				else
					out_buf = makeByteArray(serverout, serveroutlen);
				last_r = r;
				if(ca_flag && !ca_done) {
					ca_done = true;
					ca_skip = true;
					return AuthCheck;
				}
			}
			ca_skip = false;
			if(last_r == SASL_OK) {
				getssfparams();
				return Success;
			}
			return Continue;
		}
	}
Example #13
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;
int rc, firsttime=1, clen;
unsigned int inlen, outlen;

input=data;
inlen=Ustrlen(data);

HDEBUG(D_auth) debug=string_copy(data);

hname=expand_string(ob->server_hostname);
if(hname == NULL)
  {
  auth_defer_msg = expand_string_message;
  return DEFER;
  }

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

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

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

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

rc=SASL_CONTINUE;

while(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=store_get(outlen+1);
    memcpy(out2,output,outlen);
    out2[outlen]='\0';
    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)
      {
      clen=auth_b64decode(input, &clear);
      if(clen < 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;
    }
  else if( rc==SASL_FAIL     || rc==SASL_BUFOVER
       || rc==SASL_BADMAC   || rc==SASL_BADAUTH
       || rc==SASL_NOAUTHZ  || rc==SASL_ENCRYPT
       || rc==SASL_EXPIRED  || rc==SASL_DISABLED
       || rc==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;
    }
  else if(rc==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;
    }
  else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
    {
    /* 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;
    }
  else if(rc==SASL_OK)
    {
    /* Get the username and copy it into $auth1 and $1. The former is now the
    preferred variable; the latter is the original variable. */
    rc = sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2));
    auth_vars[0] = expand_nstring[1] = string_copy(out2);
    expand_nlength[1] = Ustrlen(expand_nstring[1]);
    expand_nmax = 1;

    HDEBUG(D_auth)
      debug_printf("Cyrus SASL %s authentication succeeded for %s\n", ob->server_mech, out2);
    /* 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);
    }
  }
/* NOTREACHED */
return 0;  /* Stop compiler complaints */
}
Example #14
0
int
ArgusAuthenticateClient (struct ArgusClientData *client)
{
   int retn = 1;

#ifdef ARGUS_SASL
   unsigned int rlen = 0;
   int len, mechnum = 0;
   char buf[8192], chosenmech[512];
   const char *data;
   sasl_conn_t *conn = NULL;

// int SASLOpts = (SASL_SEC_NOPLAINTEXT | SASL_SEC_NOANONYMOUS);
   FILE *in, *out;

   conn = client->sasl_conn;

   if ((retn = sasl_listmech(conn, NULL, "{", ", ", "}", &data, &rlen, &mechnum)) != SASL_OK)
      ArgusLog (LOG_ERR, "ArgusAuthenticateClient: Error generating mechanism list");

   if ((in  = fdopen (client->fd, "r")) < 0)
      ArgusLog (LOG_ERR, "ArgusAuthenticateClient: fdopen() error %s", strerror(errno));

   if ((out = fdopen (client->fd, "w")) < 0)
      ArgusLog (LOG_ERR, "ArgusAuthenticateClient: fdopen() error %s", strerror(errno));

   ArgusSendSaslString (out, data, rlen, SASL_OK);

   if ((len = ArgusGetSaslString (in, chosenmech, sizeof(chosenmech))) <= 0)  {
#ifdef ARGUSDEBUG
      ArgusDebug (2, "ArgusAuthenticateClient: Error ArgusGetSaslString returned %d\n", len);
#endif
      return 0;
   }

   if ((len = ArgusGetSaslString (in, buf, sizeof(buf))) <= 0)  {
#ifdef ARGUSDEBUG
      ArgusDebug (2, "ArgusAuthenticateClient: Error ArgusGetSaslString returned %d\n", len);
#endif
      return 0;
   }

   if (*buf == 'Y') {
      if ((len = ArgusGetSaslString (in, buf, sizeof(buf))) <= 0)  {
#ifdef ARGUSDEBUG
         ArgusDebug (2, "ArgusAuthenticateClient: Error ArgusGetSaslString returned %d\n", len);
#endif
         return 0;
      }
      retn = sasl_server_start(conn, chosenmech, buf, len, &data, &rlen);

   } else {
      retn = sasl_server_start(conn, chosenmech, NULL, 0, &data, &rlen);
   }

   if ((retn != SASL_OK) && (retn != SASL_CONTINUE)) {
      sprintf (buf, "%s", sasl_errstring(retn, NULL, NULL));
#ifdef ARGUSDEBUG
      ArgusDebug (2, "ArgusAuthenticateClient: Error starting SASL negotiation");
#endif
      ArgusSendSaslString(out, buf, strlen(buf), retn);
      return 0;
   }

   while (retn == SASL_CONTINUE) {
      if (data) {
#ifdef ARGUSDEBUG
         ArgusDebug(2, "sending response length %d...\n", rlen);
#endif
         ArgusSendSaslString(out, data, rlen, retn);
      } else {
#ifdef ARGUSDEBUG
         ArgusDebug(2, "no data to send? ...\n");
#endif
      }

#ifdef ARGUSDEBUG
      ArgusDebug(2, "waiting for client reply...\n");
#endif
      len = ArgusGetSaslString(in, buf, sizeof(buf));

      if (len < 0) {
#ifdef ARGUSDEBUG
         ArgusDebug(2, "client disconnected ...\n");
#endif
         return 0;
      }

      retn = sasl_server_step(conn, buf, len, &data, &rlen);
      if ((retn != SASL_OK) && (retn != SASL_CONTINUE)) {
         sprintf (buf, "%s", sasl_errstring(retn, NULL, NULL));
#ifdef ARGUSDEBUG
         ArgusDebug(2, "Authentication failed %s\n", sasl_errstring(retn, NULL, NULL));
#endif
         ArgusSendSaslString(out, buf, strlen(buf), retn);
         return 0;
      }
   }

   if (retn == SASL_OK)
      ArgusSendSaslString(out, NULL, 0, SASL_OK);

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

   return (retn);
}
Example #15
0
static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len)
{
    uint32_t datalen = len;
    const char *serverout;
    unsigned int serveroutlen;
    int err;
    char *clientdata = NULL;

    /* NB, distinction of NULL vs "" is *critical* in SASL */
    if (datalen) {
        clientdata = (char*)data;
        clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */
        datalen--; /* Don't count NULL byte when passing to _start() */
    }

    VNC_DEBUG("Step using SASL Data %p (%d bytes)\n",
              clientdata, datalen);
    err = sasl_server_step(vs->sasl.conn,
                           clientdata,
                           datalen,
                           &serverout,
                           &serveroutlen);
    if (err != SASL_OK &&
        err != SASL_CONTINUE) {
        VNC_DEBUG("sasl step failed %d (%s)\n",
                  err, sasl_errdetail(vs->sasl.conn));
        sasl_dispose(&vs->sasl.conn);
        vs->sasl.conn = NULL;
        goto authabort;
    }

    if (serveroutlen > SASL_DATA_MAX_LEN) {
        VNC_DEBUG("sasl step reply data too long %d\n",
                  serveroutlen);
        sasl_dispose(&vs->sasl.conn);
        vs->sasl.conn = NULL;
        goto authabort;
    }

    VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
              serveroutlen, serverout ? 0 : 1);

    if (serveroutlen) {
        vnc_write_u32(vs, serveroutlen + 1);
        vnc_write(vs, serverout, serveroutlen + 1);
    } else {
        vnc_write_u32(vs, 0);
    }

    /* Whether auth is complete */
    vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);

    if (err == SASL_CONTINUE) {
        VNC_DEBUG("%s", "Authentication must continue\n");
        /* Wait for step length */
        vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
    } else {
        if (!vnc_auth_sasl_check_ssf(vs)) {
            VNC_DEBUG("Authentication rejected for weak SSF %p\n", vs->ioc);
            goto authreject;
        }

        /* Check username whitelist ACL */
        if (vnc_auth_sasl_check_access(vs) < 0) {
            VNC_DEBUG("Authentication rejected for ACL %p\n", vs->ioc);
            goto authreject;
        }

        VNC_DEBUG("Authentication successful %p\n", vs->ioc);
        vnc_write_u32(vs, 0); /* Accept auth */
        /*
         * Delay writing in SSF encoded mode until pending output
         * buffer is written
         */
        if (vs->sasl.runSSF)
            vs->sasl.waitWriteSSF = vs->output.offset;
        start_client_init(vs);
    }

    return 0;

 authreject:
    vnc_write_u32(vs, 1); /* Reject auth */
    vnc_write_u32(vs, sizeof("Authentication failed"));
    vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
    vnc_flush(vs);
    vnc_client_error(vs);
    return -1;

 authabort:
    vnc_client_error(vs);
    return -1;
}
Example #16
0
/*
 * Continue next SASL negotiation with client.
 */
DWORD
VmDirSASLSessionStep(
    PVDIR_SASL_BIND_INFO    pSaslBindInfo,
    PVDIR_BIND_REQ          pBindReq,
    PVDIR_BERVALUE          pBervSaslReply,
    PVDIR_LDAP_RESULT       pResult
    )
{
    DWORD               dwError = 0;
    PVOID               pOutBlob = NULL;
    unsigned            iRespLen = 0;
    PCSTR               pszRespBlob = NULL;

    dwError = sasl_server_step(     pSaslBindInfo->pSaslCtx,
                                    pBindReq->cred.lberbv.bv_val,
                                    (unsigned) pBindReq->cred.lberbv.bv_len,
                                    &pszRespBlob,
                                    &iRespLen);
    if (dwError == SASL_OK)
    {
        dwError = _VmDirSASLGetCtxProps(pSaslBindInfo);
        BAIL_ON_VMDIR_ERROR(dwError);

        dwError = LDAP_SUCCESS;
        pSaslBindInfo->saslStatus = SASL_STATUS_DONE;
    }
    else if (dwError == SASL_CONTINUE)
    {
        pSaslBindInfo->saslStatus = SASL_STATUS_IN_PROGRESS;

        if (iRespLen > 0)
        {
            dwError = VmDirAllocateAndCopyMemory((PVOID)pszRespBlob, iRespLen, &pOutBlob);
            BAIL_ON_VMDIR_ERROR(dwError);

            pBervSaslReply->lberbv.bv_val = pOutBlob;
            pBervSaslReply->lberbv.bv_len = iRespLen;
            pBervSaslReply->bOwnBvVal = TRUE;
        }
        else
        {
            pBervSaslReply->lberbv.bv_len = 0;
            pBervSaslReply->lberbv.bv_val = "";
            pBervSaslReply->bOwnBvVal = FALSE;
        }

        dwError = LDAP_SUCCESS;
        pResult->errCode = LDAP_SASL_BIND_IN_PROGRESS;
        pResult->replyInfo.type = REP_SASL;
    }
    else
    {
        BAIL_ON_SASL_ERROR(dwError);
    }

cleanup:

    return dwError;

error:

    VMDIR_SAFE_FREE_MEMORY(pOutBlob);

    pBervSaslReply->lberbv.bv_val = NULL;
    pBervSaslReply->lberbv.bv_len = 0;
    pBervSaslReply->bOwnBvVal = FALSE;

    goto cleanup;

sasl_error:

    VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL,
                     "SASLSessionStep: sasl error (%d)(%s)",
                     dwError,
                     VDIR_SAFE_STRING(sasl_errdetail(pSaslBindInfo->pSaslCtx)) );

    dwError = _VmDirSASLToLDAPError(dwError);
    if (dwError == LDAP_INVALID_CREDENTIALS )
    {
        _VmDirSASLGetCtxProps(pSaslBindInfo);
        // ignore error
    }

    goto error;
}