/** * Called to retrive the mechlist from the packet. * @return 0 to continue authentication, 1 if no authentication needed, or * -1 on error. */ static int set_chosen_mech(mc_pSESSREQ sreq, lcb_string *mechlist, const char **data, unsigned int *ndata) { cbsasl_error_t saslerr; const char *chosenmech; mc_pSESSINFO ctx = sreq->inner; lcb_assert(sreq->inner); if (ctx->settings->sasl_mech_force) { char *forcemech = ctx->settings->sasl_mech_force; if (!strstr(mechlist->base, forcemech)) { /** Requested mechanism not found */ set_error_ex(sreq, LCB_SASLMECH_UNAVAILABLE, mechlist->base); return -1; } lcb_string_clear(mechlist); if (lcb_string_appendz(mechlist, forcemech)) { set_error_ex(sreq, LCB_CLIENT_ENOMEM, NULL); return -1; } } saslerr = cbsasl_client_start(ctx->sasl, mechlist->base, NULL, data, ndata, &chosenmech); switch (saslerr) { case SASL_OK: ctx->nmech = strlen(chosenmech); if (! (ctx->mech = strdup(chosenmech)) ) { set_error_ex(sreq, LCB_CLIENT_ENOMEM, NULL); return -1; } return 0; case SASL_NOMECH: lcb_log(LOGARGS(sreq, INFO), SESSREQ_LOGFMT "Server does not support SASL (no mechanisms supported)", SESSREQ_LOGID(sreq)); return 1; break; default: lcb_log(LOGARGS(sreq, INFO), SESSREQ_LOGFMT "cbsasl_client_start returned %d", SESSREQ_LOGID(sreq), saslerr); set_error_ex(sreq, LCB_EINTERNAL, "Couldn't start SASL client"); return -1; } }
static void sasl_list_mech_response_handler(lcb_server_t *server, struct lcb_command_data_st *command_data, protocol_binary_response_header *res) { const char *chosenmech; char *mechlist; const char *data; unsigned int ndata; protocol_binary_request_no_extras req; lcb_size_t bodysize; lcb_uint16_t ret = ntohs(res->response.status); lcb_connection_t conn = &server->connection; if (ret != PROTOCOL_BINARY_RESPONSE_SUCCESS) { lcb_error_handler(server->instance, LCB_AUTH_ERROR, "SASL authentication failed: " "bad SASL_LIST_MECH status code"); return; } bodysize = ntohl(res->response.bodylen); mechlist = calloc(bodysize + 1, sizeof(char)); if (mechlist == NULL) { lcb_error_handler(server->instance, LCB_CLIENT_ENOMEM, NULL); return; } memcpy(mechlist, (const char *)(res + 1), bodysize); if (server->instance->sasl_mech_force) { if (!strstr(mechlist, server->instance->sasl_mech_force)) { lcb_error_handler(server->instance, LCB_SASLMECH_UNAVAILABLE, mechlist); free(mechlist); return; } /** strstr already ensures we have enough space */ strcpy(mechlist, server->instance->sasl_mech_force); } if (cbsasl_client_start(server->sasl_conn, mechlist, NULL, &data, &ndata, &chosenmech) != SASL_OK) { free(mechlist); lcb_error_handler(server->instance, LCB_AUTH_ERROR, "Unable to start sasl client"); return; } free(mechlist); server->sasl_nmech = strlen(chosenmech); server->sasl_mech = strdup(chosenmech); if (server->sasl_mech == NULL) { lcb_error_handler(server->instance, LCB_CLIENT_ENOMEM, NULL); return; } memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_SASL_AUTH; req.message.header.request.keylen = ntohs((lcb_uint16_t)server->sasl_nmech); req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.bodylen = ntohl((lcb_uint32_t)(server->sasl_nmech + ndata)); lcb_server_buffer_start_packet(server, command_data->cookie, conn->output, &server->output_cookies, req.bytes, sizeof(req.bytes)); lcb_server_buffer_write_packet(server, conn->output, server->sasl_mech, server->sasl_nmech); lcb_server_buffer_write_packet(server, conn->output, data, ndata); lcb_server_buffer_end_packet(server, conn->output); lcb_sockrw_set_want(conn, LCB_WRITE_EVENT, 0); }