CBSASL_PUBLIC_API cbsasl_error_t cbsasl_client_step(cbsasl_conn_t *conn, const char *serverin, unsigned int serverinlen, void **not_used, const char **clientout, unsigned int *clientoutlen) { unsigned char digest[DIGEST_LENGTH]; char md5string[DIGEST_LENGTH * 2]; const char *usernm = NULL; unsigned int usernmlen; cbsasl_secret_t *pass; cbsasl_error_t ret; if (conn->client == 0) { return SASL_BADPARAM; } if (conn->c.client.plain) { /* Shouldn't be called during plain auth */ return SASL_BADPARAM; } 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; } free(conn->c.client.userdata); conn->c.client.userdata = calloc(usernmlen + 1 + sizeof(md5string) + 1, 1); if (conn->c.client.userdata == NULL) { return SASL_NOMEM; } cbsasl_hmac_md5((unsigned char*)serverin, serverinlen, pass->data, pass->len, digest); cbsasl_hex_encode(md5string, (char *) digest, DIGEST_LENGTH); memcpy(conn->c.client.userdata, usernm, usernmlen); conn->c.client.userdata[usernmlen] = ' '; memcpy(conn->c.client.userdata + usernmlen + 1, md5string, sizeof(md5string)); *clientout = conn->c.client.userdata; *clientoutlen = (unsigned int)strlen(conn->c.client.userdata); return SASL_CONTINUE; }
CBSASL_PUBLIC_API cbsasl_error_t cbsasl_client_step(cbsasl_conn_t *conn, const char *serverin, unsigned int serverinlen, void **not_used, const char **clientout, unsigned int *clientoutlen) { const char *usernm = NULL; unsigned int usernmlen; cbsasl_secret_t *pass; cbsasl_error_t ret; (void)not_used; if (conn->client == 0) { return SASL_BADPARAM; } if (conn->c.client.auth_mech == SASL_AUTH_MECH_PLAIN) { /* Shouldn't be called during plain auth */ return SASL_BADPARAM; } 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; } free(conn->c.client.userdata); conn->c.client.userdata = NULL; switch (conn->c.client.auth_mech) { case SASL_AUTH_MECH_CRAM_MD5: { unsigned char digest[DIGEST_LENGTH]; char md5string[DIGEST_LENGTH * 2]; conn->c.client.userdata = calloc(usernmlen + 1 + sizeof(md5string) + 1, 1); if (conn->c.client.userdata == NULL) { return SASL_NOMEM; } cbsasl_hmac_md5((unsigned char *)serverin, serverinlen, pass->data, pass->len, digest); cbsasl_hex_encode(md5string, (char *)digest, DIGEST_LENGTH); memcpy(conn->c.client.userdata, usernm, usernmlen); conn->c.client.userdata[usernmlen] = ' '; memcpy(conn->c.client.userdata + usernmlen + 1, md5string, sizeof(md5string)); break; } case SASL_AUTH_MECH_SCRAM_SHA1: case SASL_AUTH_MECH_SCRAM_SHA256: case SASL_AUTH_MECH_SCRAM_SHA512: { if (!conn->c.client.auth_message) { const char *combinednonce = NULL; // nonce extracted from server's first reply unsigned int noncelen = 0; const char *salt = NULL; // salt extracted from server's first reply unsigned int saltlen = 0; unsigned int itcount = 0; unsigned char saltedpassword[CBSASL_SHA512_DIGEST_SIZE]; // max digest size unsigned int saltedpasslen = 0; unsigned int prooflen = 0; // proof size in base64 ret = parse_server_challenge(serverin, serverinlen, &combinednonce, &noncelen, &salt, &saltlen, &itcount); if (ret != SASL_OK) { return ret; } if (!combinednonce || !noncelen || !salt || !saltlen || !itcount) { // missing or invalid value in server challenge return SASL_BADPARAM; } if (noncelen < SCRAM_NONCE_SIZE * 2 || memcmp(combinednonce, conn->c.client.nonce, SCRAM_NONCE_SIZE * 2)) { // the combined nonce doesn't start with the client nonce we sent previously return SASL_BADPARAM; } // ok, now we can compute the client proof ret = generate_salted_password(conn->c.client.auth_mech, pass, salt, saltlen, itcount, saltedpassword, &saltedpasslen); if (ret != SASL_OK) { return ret; } // save salted password for later use conn->c.client.saltedpassword = calloc(saltedpasslen, 1); if (conn->c.client.saltedpassword == NULL) { return SASL_NOMEM; } memcpy(conn->c.client.saltedpassword, saltedpassword, saltedpasslen); conn->c.client.saltedpasslen = saltedpasslen; // before building the client proof, we start building the client final message, // as it is used for the computation of the proof // The final message starts with the base64-encoded GS2 header from the initial message. // As we always use "n,,", we can hardcode directly its base64-counterpart, so "biws". #define FINAL_HEADER "c=biws,r=" #define PROOF_ATTR ",p=" switch (conn->c.client.auth_mech) { case SASL_AUTH_MECH_SCRAM_SHA1: prooflen = (CBSASL_SHA1_DIGEST_SIZE / 3 + 1) * 4; break; case SASL_AUTH_MECH_SCRAM_SHA256: prooflen = (CBSASL_SHA256_DIGEST_SIZE / 3 + 1) * 4; break; case SASL_AUTH_MECH_SCRAM_SHA512: prooflen = (CBSASL_SHA512_DIGEST_SIZE / 3 + 1) * 4; break; default: break; } conn->c.client.userdata = calloc(strlen(FINAL_HEADER) + noncelen + strlen(PROOF_ATTR) + prooflen + 1, 1); if (conn->c.client.userdata == NULL) { return SASL_NOMEM; } memcpy(conn->c.client.userdata, FINAL_HEADER, strlen(FINAL_HEADER)); memcpy(conn->c.client.userdata + strlen(FINAL_HEADER), combinednonce, noncelen); memcpy(conn->c.client.userdata + strlen(FINAL_HEADER) + noncelen, PROOF_ATTR, strlen(PROOF_ATTR)); ret = compute_client_proof( conn->c.client.auth_mech, saltedpassword, saltedpasslen, conn->c.client.client_first_message_bare, strlen(conn->c.client.client_first_message_bare), serverin, serverinlen, conn->c.client.userdata, strlen(FINAL_HEADER) + noncelen, &(conn->c.client.auth_message), conn->c.client.userdata + strlen(FINAL_HEADER) + noncelen + strlen(PROOF_ATTR), prooflen + 1); if (ret != SASL_OK) { return ret; } } else { // auth_message should not be already set return SASL_FAIL; } break; } default: break; } *clientout = conn->c.client.userdata; *clientoutlen = strlen(conn->c.client.userdata); return SASL_CONTINUE; }