Esempio n. 1
0
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;
}
Esempio n. 2
0
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;
}