Protocol::Protocol (int type, string logfile)
{
	log = logfile;
        LOG(log, "** Protocol Constructor **\n");
        
        if (type == CLIENT) {
                int fd = open("key_client", O_RDONLY);
                read(fd, (void *) &client_id, sizeof(uint32_t));
                read(fd, (void *) shared_key, EVP_MAX_KEY_LENGTH);
                close(fd);
                generate_nonce((char *) &client_nonce);

                // log
                LOG(log, "\t\tClient ID: " + utos(client_id) + "\n");
                LOG(log, "\t\tShared key: " +
                    stringbyte(shared_key, EVP_MAX_KEY_LENGTH) + "\n");
                LOG(log, "\t\tClient nonce: " + utos(client_nonce) + "\n");
        }
        server_id = ID_SERVER;
        md_ctx = create_md_context();
        md_len = EVP_MD_size(md_ctx->digest);

        // log
        LOG(log, "\t\tServer ID: " + utos(server_id) + "\n");
        LOG(log, "** End constructor **\n\n");
}
Exemplo n.º 2
0
static void
try_connect (server_auth_t * auth)
{
    int     f;
    CONNECTION *cli;
    unsigned int ip;

    /* attempt a connection.  we do this nonblocking so that the server
       doesn't halt if it takes a long time to connect */
    f = make_tcp_connection (auth->name, auth->port, &ip);
    if (f == -1)
	return;

    cli = new_connection ();
    if (!cli)
	goto error;
    cli->fd = f;
    cli->host = STRDUP (auth->alias ? auth->alias : auth->name);
    if (!cli->host)
    {
	OUTOFMEMORY ("try_connect");
	goto error;
    }
    cli->server_login = 1;
    if ((cli->opt.auth = CALLOC (1, sizeof (AUTH))) == 0)
    {
	OUTOFMEMORY ("try_connect");
	goto error;
    }
    cli->opt.auth->nonce = generate_nonce ();
    if (!cli->opt.auth->nonce)
    {
	log_message ("try_connect: could not generate nonce, closing connection");
	goto error;
    }
    cli->ip = BSWAP32 (ip);
    cli->port = auth->port;

    if (add_client (cli, 1/* server connection */))
	goto error;

    return;
  error:
    log_message ("try_connect: closing connection");
    if (cli)
    {
	CLOSE (cli->fd);
	if (cli->host)
	    FREE (cli->host);
	if (cli->opt.auth)
	{
	    if (cli->opt.auth->nonce)
		FREE (cli->opt.auth->nonce);
	    FREE (cli->opt.auth);
	}
	FREE (cli);
    }
}
Exemplo n.º 3
0
static void challenge(char **challenge, unsigned *challengelen)
{
    char nonce[NONCE_LENGTH];
    generate_nonce(nonce);
    *challenge = (char *)malloc(CHALLENGE_LENGTH * sizeof(char));
    memcpy((void *)((*challenge)), CHALLENGE_TEMPLATE, CHALLENGE_LENGTH);
    memcpy((void *)((*challenge) + 1), nonce, NONCE_LENGTH);
    *challengelen = CHALLENGE_LENGTH;
}
Exemplo n.º 4
0
const char *
heim_digest_generate_challenge(heim_digest_t context)
{
    char *challenge = NULL;
    
    if (context->serverRealm == NULL)
	return NULL;
    
    if (context->serverNonce == NULL) {
	if ((context->serverNonce = generate_nonce()) == NULL)
	    return NULL;
    }
    
    if (context->serverQOP == NULL) {
	if ((context->serverQOP = strdup("auth")) == NULL)
	    return NULL;
    }
    
    if (context->serverMaxbuf == NULL) {
	if ((context->serverMaxbuf = strdup("65536")) == NULL)
	    return NULL;
    }

    switch(context->type) {
	case HEIM_DIGEST_TYPE_RFC2617_MD5:
	    asprintf(&challenge, "realm=\"%s\",nonce=\"%s\",algorithm=md5,qop=\"%s\"",
		     context->serverRealm, context->serverNonce,
		     context->serverQOP);
	    break;
	case HEIM_DIGEST_TYPE_RFC2617_MD5_SESS:
	    asprintf(&challenge, "realm=\"%s\",nonce=\"%s\",algorithm=md5-sess,qop=\"%s\"",
		     context->serverRealm, context->serverNonce, context->serverQOP);
	    break;
	case HEIM_DIGEST_TYPE_RFC2069:
	    asprintf(&challenge, "realm=\"%s\",nonce=\"%s\"",
		     context->serverRealm, context->serverNonce);
	    break;
	case HEIM_DIGEST_TYPE_AUTO:
	    context->type = HEIM_DIGEST_TYPE_RFC2831;
	    /* FALL THOUGH */
	case HEIM_DIGEST_TYPE_RFC2831:
	    asprintf(&challenge, "realm=\"%s\",nonce=\"%s\",qop=\"%s\",algorithm=md5-sess,charset=utf-8,maxbuf=%s",
		     context->serverRealm, context->serverNonce, context->serverQOP, context->serverMaxbuf);
	    break;
    }

    FREE_AND_CLEAR(context->serverChallenge);
    context->serverChallenge = challenge;
    
    return challenge;
}
Exemplo n.º 5
0
void new_connection(struct config *conf)
{
    struct conn_state *st = calloc(1, sizeof(struct conn_state));
    st->state = WAIT_HELLO;
    st->conf = conf;

    st->sock = socket(AF_INET, SOCK_STREAM, 0);
    int optval = 1;
    setsockopt(st->sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

    evutil_make_socket_nonblocking(st->sock);
    struct bufferevent *bev;
    bev = bufferevent_socket_new(conf->base, st->sock, BEV_OPT_CLOSE_ON_FREE);
    // TODO: error check
    struct timeval read_to;
    read_to.tv_sec = TIMEOUT_SECS;
    read_to.tv_usec = 0;
    bufferevent_set_timeouts(bev, &read_to, &read_to);
    st->bev = bev;

    // Generate client random
    generate_nonce(conf, st->nonce);
    generate_client_random(conf->prev_block_hash, conf->merkle_root,
                           st->nonce, st->client_random);

    // Generte client hello and send it
    uint8_t *client_hello;
    size_t client_hello_len = make_client_hello(st->client_random, &client_hello);

    evbuffer_add(bufferevent_get_output(bev), client_hello, client_hello_len);
    free(client_hello);

    bufferevent_setcb(bev, readcb, NULL, eventcb, st);
    // TODO: error check
    bufferevent_enable(bev, EV_READ | EV_WRITE);

    if (bufferevent_socket_connect(bev, (struct sockaddr *)&conf->sin,
                                   sizeof(conf->sin)) < 0) {

        perror("socket connected failed: ");
        printf("Run `sudo sysctl -w net.ipv4.tcp_tw_recycle=1`\n");
        cleanup(st);
    }

    conf->num_connections++;
}
Exemplo n.º 6
0
const char *
heim_digest_create_response(heim_digest_t context, char **response)
{
    char *a1, *str, *cnonce = NULL, *opaque = NULL, *uri = NULL, *nc = NULL;
    
    if (response)
	*response = NULL;
    
    if (context->clientUsername == NULL || context->clientURI == NULL)
	return NULL;
    
    if (context->clientRealm == NULL) {
	if (context->serverRealm == NULL)
	    return NULL;
	if ((context->clientRealm = strdup(context->serverRealm)) == NULL)
	    return NULL;
    }
    
    if (context->type != HEIM_DIGEST_TYPE_RFC2069) {
	if (context->clientNC == NULL) {
	    if ((context->clientNC = strdup("00000001")) == NULL)
		return NULL;
	}
	if (context->clientNonce == NULL) {
	    if ((context->clientNonce = generate_nonce()) == NULL)
		return NULL;
	}

	/**
	 * If using non RFC2069, appropriate QOP should be set.
	 *
	 * Pick QOP from server if not given, if its a list, pick the first entry
	 */
	if (context->clientQOP == NULL) {
	    char *r;
	    if (context->serverQOP == NULL)
		return NULL;
	    r = strchr(context->serverQOP, ',');
	    if (r == NULL) {
		if ((context->clientQOP = strdup(context->serverQOP)) == NULL)
			return NULL;
	    } else {
		size_t len = (r - context->serverQOP) + 1;
		if ((context->clientQOP = malloc(len)) == NULL)
		    return NULL;
		strlcpy(context->clientQOP, context->serverQOP, len);
	    }
	}
    }
	    
    set_auth_method(context);
    
    a1 = build_A1_hash(context);
    if (a1 == NULL)
	return NULL;
    
    str = build_digest(context, a1, context->serverMethod);
    if (str == NULL) {
	MEMSET_FREE_AND_CLEAR(a1);
	return NULL;
    }
    
    MEMSET_FREE_AND_CLEAR(context->clientResponse);
    context->clientResponse = str;
    
    if (context->clientURI) {
	const char *name = "digest-uri";
	if (context->type != HEIM_DIGEST_TYPE_RFC2831)
	    name = "uri";
	asprintf(&uri, ",%s=\"%s\"", name, context->clientURI);
    }
    
    if (context->serverOpaque)
	asprintf(&opaque, ",opaque=\"%s\"", context->serverOpaque);
    
    if (context->clientNonce)
	asprintf(&cnonce, ",cnonce=\"%s\"", context->clientNonce);

    if (context->clientNC)
	asprintf(&nc, ",nc=%s", context->clientNC);
    
    asprintf(&context->clientReply,
	     "username=%s,realm=%s,nonce=\"%s\",qop=\"%s\"%s%s%s,response=\"%s\"%s",
	     context->clientUsername, context->clientRealm,
	     context->serverNonce,
	     context->clientQOP,
	     uri ? uri : "",
	     cnonce ? cnonce : "",
	     nc ? nc : "",
	     context->clientResponse,
	     opaque ? opaque : "");
    
    build_server_response(context, a1, response);
    MEMSET_FREE_AND_CLEAR(a1);
    FREE_AND_CLEAR(uri);
    FREE_AND_CLEAR(opaque);
    FREE_AND_CLEAR(cnonce);
    FREE_AND_CLEAR(nc);
    
    return context->clientReply;
}
Exemplo n.º 7
0
int nterfacer_line_event(struct esocket *sock, char *newline) {
  struct sconnect *socket = sock->tag;
  char *response, *theirnonceh = NULL, *theirivh = NULL;
  unsigned char theirnonce[16], theiriv[16];
  int number, reason;

  switch(socket->status) {
    case SS_IDLE:
      if(strcasecmp(newline, ANTI_FULL_VERSION)) {
        nterface_log(nrl, NL_INFO, "Protocol mismatch from %s: %s", socket->permit->hostname->content, newline);
        return 1;
      } else {
        unsigned char challenge[32];
        char ivhex[16 * 2 + 1], noncehex[16 * 2 + 1];

        if(!get_entropy(challenge, 32) || !get_entropy(socket->iv, 16)) {
          nterface_log(nrl, NL_ERROR, "Unable to open challenge/IV entropy bin!");
          return 1;
        }

        int_to_hex(challenge, socket->challenge, 32);
        int_to_hex(socket->iv, ivhex, 16);

        memcpy(socket->response, challenge_response(socket->challenge, socket->permit->password->content), sizeof(socket->response));
        socket->response[sizeof(socket->response) - 1] = '\0'; /* just in case */

        socket->status = SS_VERSIONED;
        if(!generate_nonce(socket->ournonce, 1)) {
          nterface_log(nrl, NL_ERROR, "Unable to generate nonce!");
          return 1;
        }
        int_to_hex(socket->ournonce, noncehex, 16);

        if(esocket_write_line(sock, "%s %s %s", socket->challenge, ivhex, noncehex))
           return BUF_ERROR;
        return 0;
      }
      break;
    case SS_VERSIONED:
      for(response=newline;*response;response++) {
        if((*response == ' ') && (*(response + 1))) {
          *response = '\0';
          theirivh = response + 1;
          break;
        }
      }

      if(theirivh) {
        for(response=theirivh;*response;response++) {
          if((*response == ' ') && (*(response + 1))) {
            *response = '\0';
            theirnonceh = response + 1;
            break;
          }
        }
      }

      if(!theirivh || (strlen(theirivh) != 32) || !hex_to_int(theirivh, theiriv, sizeof(theiriv)) ||
         !theirnonceh || (strlen(theirnonceh) != 32) || !hex_to_int(theirnonceh, theirnonce, sizeof(theirnonce))) {
        nterface_log(nrl, NL_INFO, "Protocol error drop: %s", socket->permit->hostname->content);
        return 1;
      }

      if(!memcmp(socket->ournonce, theirnonce, sizeof(theirnonce))) {
        nterface_log(nrl, NL_INFO, "Bad nonce drop: %s", socket->permit->hostname->content);
        return 1;
      }

      if(!strncasecmp(newline, socket->response, sizeof(socket->response))) {
        unsigned char theirkey[32], ourkey[32];

        derive_key(ourkey, socket->permit->password->content, socket->challenge, socket->ournonce, theirnonce, (unsigned char *)"SERVER", 6);

        derive_key(theirkey, socket->permit->password->content, socket->response, theirnonce, socket->ournonce, (unsigned char *)"CLIENT", 6);
        nterface_log(nrl, NL_INFO, "Authed: %s", socket->permit->hostname->content);
        socket->status = SS_AUTHENTICATED;
        switch_buffer_mode(sock, ourkey, socket->iv, theirkey, theiriv);

        if(esocket_write_line(sock, "Oauth"))
          return BUF_ERROR;
      } else {
        nterface_log(nrl, NL_INFO, "Bad CR drop: %s", socket->permit->hostname->content);
        
        return 1;
      }
      break;
    case SS_AUTHENTICATED:
      nterface_log(nrl, NL_INFO|NL_LOG_ONLY, "L(%s): %s", socket->permit->hostname->content, newline);
      reason = nterfacer_new_rline(newline, sock, &number);
      if(reason) {
        if(reason == RE_SOCKET_ERROR)
          return BUF_ERROR;
        if(reason != RE_BAD_LINE) {
          if(esocket_write_line(sock, "%d,E%d,%s", number, reason, request_error(reason)))
            return BUF_ERROR;
          return 0;
        } else {
          return 1;
        }
      }
      break;
  }

  return 0;
}
Exemplo n.º 8
0
/* Construct MDP packet frame from overlay_mdp_frame structure
   (need to add return address from bindings list, and copy
   payload etc).
   This is for use by the SERVER. 
   Clients should use overlay_mdp_send()
 */
int overlay_mdp_dispatch(overlay_mdp_frame *mdp,int userGeneratedFrameP,
			 struct sockaddr_un *recvaddr,int recvaddrlen)
{
  IN();

  /* Prepare the overlay frame for dispatch */
  struct overlay_frame *frame = calloc(1,sizeof(struct overlay_frame));
  if (!frame)
    FATAL("Couldn't allocate frame buffer");
  
  if (is_sid_any(mdp->out.src.sid)){
    /* set source to ourselves */
    frame->source = my_subscriber;
    bcopy(frame->source->sid, mdp->out.src.sid, SID_SIZE);
  }else if (is_sid_broadcast(mdp->out.src.sid)){
    /* This is rather naughty if it happens, since broadcasting a
     response can lead to all manner of nasty things.
     Picture a packet with broadcast as the source address, sent
     to, say, the MDP echo port on another node, and with a source
     port also of the echo port.  Said echo will get multiplied many,
     many, many times over before the TTL finally reaches zero.
     So we just say no to any packet with a broadcast source address. 
     (Of course we have other layers of protection against such 
     shenanigens, such as using BPIs to smart-flood broadcasts, but
     security comes through depth.)
     */
    op_free(frame);
    RETURN(WHY("Packet had broadcast address as source address"));
  }else{
    // assume all local identities have already been unlocked and marked as SELF.
    frame->source = find_subscriber(mdp->out.src.sid, SID_SIZE, 0);
    if (!frame->source){
      op_free(frame);
      RETURN(WHYF("Possible spoofing attempt, tried to send a packet from %s, which is an unknown SID", alloca_tohex_sid(mdp->out.src.sid)));
    }
    if (frame->source->reachable!=REACHABLE_SELF){
      op_free(frame);
      RETURN(WHYF("Possible spoofing attempt, tried to send a packet from %s", alloca_tohex_sid(mdp->out.src.sid)));
    }
  }
  
  /* Work out if destination is broadcast or not */
  if (overlay_mdp_check_binding(frame->source, mdp->out.src.port, userGeneratedFrameP,
				recvaddr, recvaddrlen)){
    op_free(frame);
    RETURN(overlay_mdp_reply_error
	   (mdp_named.poll.fd,
	    (struct sockaddr_un *)recvaddr,
	    recvaddrlen,8,
	    "Source address is invalid (you must bind to a source address before"
	    " you can send packets"));
  }
  
  if (is_sid_broadcast(mdp->out.dst.sid)){
    /* broadcast packets cannot be encrypted, so complain if MDP_NOCRYPT
     flag is not set. Also, MDP_NOSIGN must also be applied, until
     NaCl cryptobox keys can be used for signing. */	
    if (!(mdp->packetTypeAndFlags&MDP_NOCRYPT)){
      op_free(frame);
      RETURN(overlay_mdp_reply_error(mdp_named.poll.fd,
				     recvaddr,recvaddrlen,5,
				     "Broadcast packets cannot be encrypted "));
    }
    overlay_broadcast_generate_address(&frame->broadcast_id);
    frame->destination = NULL;
  }else{
    frame->destination = find_subscriber(mdp->out.dst.sid, SID_SIZE, 1);
  }
  
  frame->ttl = mdp->out.ttl;
  if (frame->ttl == 0) 
    frame->ttl = PAYLOAD_TTL_DEFAULT;
  else if (frame->ttl > PAYLOAD_TTL_MAX) {
    op_free(frame);
    RETURN(overlay_mdp_reply_error(mdp_named.poll.fd,
				    recvaddr,recvaddrlen,9,
				    "TTL out of range"));
  }
  
  if (!frame->destination || frame->destination->reachable == REACHABLE_SELF)
    {
      /* Packet is addressed such that we should process it. */
      overlay_saw_mdp_frame(NULL,mdp,gettime_ms());
      if (frame->destination) {
	/* Is local, and is not broadcast, so shouldn't get sent out
	   on the wire. */
	op_free(frame);
	RETURN(0);
      }
    }
  
  frame->type=OF_TYPE_DATA;
  frame->prev=NULL;
  frame->next=NULL;
  struct overlay_buffer *plaintext=ob_new();
  
  if (overlay_mdp_encode_ports(plaintext, mdp->out.dst.port, mdp->out.src.port)){
    ob_free(plaintext);
    RETURN (-1);
  }
  
  if (ob_append_bytes(plaintext, mdp->out.payload, mdp->out.payload_length)){
    ob_free(plaintext);
    RETURN(-1);
  }
  
  /* Work out the disposition of the frame->  For now we are only worried
     about the crypto matters, and not compression that may be applied
     before encryption (since applying it after is useless as ciphered
     text should have maximum entropy). */
  switch(mdp->packetTypeAndFlags&(MDP_NOCRYPT|MDP_NOSIGN)) {
  case 0: /* crypted and signed (using CryptoBox authcryption primitive) */
    frame->modifiers=OF_CRYPTO_SIGNED|OF_CRYPTO_CIPHERED;
    {
      int nm=crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES;
      int zb=crypto_box_curve25519xsalsa20poly1305_ZEROBYTES;
      int nb=crypto_box_curve25519xsalsa20poly1305_NONCEBYTES;
      int cz=crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES;
      
      /* generate plain message with zero bytes and get ready to cipher it */
      int cipher_len=ob_position(plaintext);
      
      // TODO, add support for leading zero's in overlay_buffer's, then we don't need to copy the plain text again.
      unsigned char plain[zb+cipher_len];
      
      /* zero bytes */
      bzero(&plain[0],zb);
      bcopy(ob_ptr(plaintext),&plain[zb],cipher_len);
      
      cipher_len+=zb;
      
      ob_free(plaintext);
      
      frame->payload = ob_new();
      
      unsigned char *nonce = ob_append_space(frame->payload, nb+cipher_len);
      unsigned char *cipher_text = nonce + nb;
      if (!nonce)
	RETURN(-1);
      if (generate_nonce(nonce,nb)) {
	op_free(frame);
	RETURN(WHY("generate_nonce() failed to generate nonce"));
      }
      // reserve the high bit of the nonce as a flag for transmitting a shorter nonce.
      nonce[0]&=0x7f;
      
      /* get pre-computed PKxSK bytes (the slow part of auth-cryption that can be
	 retained and reused, and use that to do the encryption quickly. */
      unsigned char *k=keyring_get_nm_bytes(mdp->out.src.sid, mdp->out.dst.sid);
      if (!k) {
	op_free(frame);
	RETURN(WHY("could not compute Curve25519(NxM)")); 
      }
      /* Actually authcrypt the payload */
      if (crypto_box_curve25519xsalsa20poly1305_afternm
	  (cipher_text,plain,cipher_len,nonce,k)){
	op_free(frame);
	RETURN(WHY("crypto_box_afternm() failed")); 
      }
      if (0) {
	DEBUG("authcrypted mdp frame");
	dump("nm",k,nm);
	dump("plain text",plain,sizeof(plain));
	dump("nonce",nonce,nb);
	dump("cipher text",cipher_text,cipher_len);
      }
      /* now shuffle down to get rid of the temporary space that crypto_box
       uses. 
       TODO extend overlay buffer so we don't need this.
       */
      bcopy(&cipher_text[cz],&cipher_text[0],cipher_len-cz);
      frame->payload->position-=cz;
      if (0){
	dump("frame",&frame->payload->bytes[0],
	     frame->payload->position);
      }
    }
    break;
      
  case MDP_NOCRYPT: 
    /* Payload is sent unencrypted, but signed. */
    frame->modifiers=OF_CRYPTO_SIGNED;
    frame->payload = plaintext;
    ob_makespace(frame->payload,SIGNATURE_BYTES);
    if (crypto_sign_message(frame->source, frame->payload->bytes, frame->payload->allocSize, &frame->payload->position)){
      op_free(frame);
      RETURN(-1);
    }
    break;
      
  case MDP_NOSIGN|MDP_NOCRYPT: /* clear text and no signature */
    frame->modifiers=0; 
    frame->payload = plaintext;
    break;
  case MDP_NOSIGN: 
  default:
    /* ciphered, but not signed.
     This means we don't use CryptoBox, but rather a more compact means
     of representing the ciphered stream segment.
     */
    op_free(frame);
    RETURN(WHY("Not implemented"));
  }
  
  frame->queue=mdp->out.queue;
  if (frame->queue==0)
    frame->queue = OQ_ORDINARY;
  
  if (overlay_payload_enqueue(frame))
    op_free(frame);
  RETURN(0);
  OUT();
}
Exemplo n.º 9
0
CBSASL_PUBLIC_API
cbsasl_error_t cbsasl_client_start(cbsasl_conn_t *conn, const char *mechlist, void **prompt_need,
                                   const char **clientout, unsigned int *clientoutlen, const char **mech,
                                   int allow_scram_sha)
{
    if (conn->client == 0) {
        return SASL_BADPARAM;
    }

    *mech = NULL;
    if (allow_scram_sha) {
#if !defined(LCB_NO_SSL) && defined(HAVE_PKCS5_PBKDF2_HMAC)
        // we use SCRAM-SHA only if OpenSSL is linked and has support for PBKDF2_HMAC functions
        if (strstr(mechlist, MECH_SCRAM_SHA512) != NULL) {
            *mech = MECH_SCRAM_SHA512;
            conn->c.client.auth_mech = SASL_AUTH_MECH_SCRAM_SHA512;
        } else if (strstr(mechlist, MECH_SCRAM_SHA256) != NULL) {
            *mech = MECH_SCRAM_SHA256;
            conn->c.client.auth_mech = SASL_AUTH_MECH_SCRAM_SHA256;
        } else if (strstr(mechlist, MECH_SCRAM_SHA1) != NULL) {
            *mech = MECH_SCRAM_SHA1;
            conn->c.client.auth_mech = SASL_AUTH_MECH_SCRAM_SHA1;
        }
#endif
    }
    if (*mech == NULL) {
        if (strstr(mechlist, MECH_CRAM_MD5) != NULL) {
            *mech = MECH_CRAM_MD5;
            conn->c.client.auth_mech = SASL_AUTH_MECH_CRAM_MD5;
        } else if (strstr(mechlist, MECH_PLAIN) != NULL) {
            *mech = MECH_PLAIN;
            conn->c.client.auth_mech = SASL_AUTH_MECH_PLAIN;
        } else {
            return SASL_NOMECH;
        }
    }

    switch (conn->c.client.auth_mech) {
        case SASL_AUTH_MECH_PLAIN: {
            const char *usernm = NULL;
            unsigned int usernmlen;
            cbsasl_secret_t *pass;
            cbsasl_error_t ret;

            ret = conn->c.client.get_username(conn->c.client.get_username_ctx, CBSASL_CB_USER, &usernm, &usernmlen);
            if (ret != SASL_OK) {
                return ret;
            }

            ret = conn->c.client.get_password(conn, conn->c.client.get_password_ctx, CBSASL_CB_PASS, &pass);
            if (ret != SASL_OK) {
                return ret;
            }

            conn->c.client.userdata = calloc(usernmlen + 1 + pass->len + 1, 1);
            if (conn->c.client.userdata == NULL) {
                return SASL_NOMEM;
            }

            memcpy(conn->c.client.userdata + 1, usernm, usernmlen);
            memcpy(conn->c.client.userdata + usernmlen + 2, pass->data, pass->len);
            *clientout = conn->c.client.userdata;
            *clientoutlen = (unsigned int)(usernmlen + 2 + pass->len);
            break;
        }
        case SASL_AUTH_MECH_SCRAM_SHA1:
        case SASL_AUTH_MECH_SCRAM_SHA256:
        case SASL_AUTH_MECH_SCRAM_SHA512: {
            const char *usernm = NULL;
            unsigned int usernmlen;
            int usernmspeccharsnb;
            char binnonce[SCRAM_NONCE_SIZE]; // binary nonce
            cbsasl_error_t ret;

            ret = conn->c.client.get_username(conn->c.client.get_username_ctx, CBSASL_CB_USER, &usernm, &usernmlen);
            if (ret != SASL_OK) {
                return ret;
            }
            usernmspeccharsnb = compute_special_chars(usernm, usernmlen);
            if (usernmspeccharsnb < 0) {
                // invalid characters in the username
                return SASL_BADPARAM;
            }

            generate_nonce(binnonce, SCRAM_NONCE_SIZE);
            conn->c.client.nonce = calloc(SCRAM_NONCE_SIZE * 2 + 1, 1);
            if (conn->c.client.nonce == NULL) {
                return SASL_NOMEM;
            }
            // stores binary nonce in hexadecimal format into conn->c.client.nonce array
            cbsasl_hex_encode(conn->c.client.nonce, binnonce, SCRAM_NONCE_SIZE);
            conn->c.client.nonce[SCRAM_NONCE_SIZE * 2] = '\0';

#define GS2_HEADER "n,,n=" // "no binding" + start of name attribute
#define NONCE_ATTR ",r="   // start of nonce attribute
            conn->c.client.userdata = calloc(
                strlen(GS2_HEADER) + usernmlen + usernmspeccharsnb * 2 + strlen(NONCE_ATTR) + SCRAM_NONCE_SIZE * 2, 1);
            if (conn->c.client.userdata == NULL) {
                return SASL_NOMEM;
            }

            memcpy(conn->c.client.userdata, GS2_HEADER, strlen(GS2_HEADER));
            if (!usernmspeccharsnb) {
                // no special char, we can do a direct copy
                memcpy(conn->c.client.userdata + strlen(GS2_HEADER), usernm, usernmlen);
            } else {
                // copy with substitution of special characters
                usernmcpy(conn->c.client.userdata + strlen(GS2_HEADER), usernm, usernmlen);
            }
            memcpy(conn->c.client.userdata + strlen(GS2_HEADER) + usernmlen + usernmspeccharsnb * 2, NONCE_ATTR,
                   strlen(NONCE_ATTR));
            memcpy(conn->c.client.userdata + strlen(GS2_HEADER) + usernmlen + usernmspeccharsnb * 2 +
                       strlen(NONCE_ATTR),
                   conn->c.client.nonce, SCRAM_NONCE_SIZE * 2);

            *clientout = conn->c.client.userdata;
            *clientoutlen = (unsigned int)(strlen(GS2_HEADER) + usernmlen + usernmspeccharsnb * 2 + strlen(NONCE_ATTR) +
                                           SCRAM_NONCE_SIZE * 2);

            // save the client first message for later step (without the first three characters)
            conn->c.client.client_first_message_bare = calloc(*clientoutlen - 3 + 1, 1); // +1 for the binary zero
            if (conn->c.client.client_first_message_bare == NULL) {
                return SASL_NOMEM;
            }
            memcpy(conn->c.client.client_first_message_bare, *clientout + 3, *clientoutlen - 3);
            // no need to add a final binary zero, as calloc already sets the memory to zero
            break;
        }
        case SASL_AUTH_MECH_CRAM_MD5:
            // no data in the first CRAM-MD5 message
            *clientout = NULL;
            *clientoutlen = 0;
            break;
    }

    (void)prompt_need;
    return SASL_OK;
}