svn_error_t *svn_ra_svn_cram_server(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_config_t *pwdb, const char **user, svn_boolean_t *success) { apr_status_t status; apr_uint64_t nonce; char hostbuf[APRMAXHOSTLEN + 1]; unsigned char cdigest[APR_MD5_DIGESTSIZE], sdigest[APR_MD5_DIGESTSIZE]; const char *challenge, *sep, *password; svn_ra_svn_item_t *item; svn_string_t *resp; *success = FALSE; /* Send a challenge. */ status = make_nonce(&nonce); if (!status) status = apr_gethostname(hostbuf, sizeof(hostbuf), pool); if (status) return fail(conn, pool, "Internal server error in authentication"); challenge = apr_psprintf(pool, "<%" APR_UINT64_T_FMT ".%" APR_TIME_T_FMT "@%s>", nonce, apr_time_now(), hostbuf); SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(c)", "step", challenge)); /* Read the client's response and decode it into *user and cdigest. */ SVN_ERR(svn_ra_svn__read_item(conn, pool, &item)); if (item->kind != SVN_RA_SVN_STRING) /* Very wrong; don't report failure */ return SVN_NO_ERROR; resp = item->u.string; sep = strrchr(resp->data, ' '); if (!sep || resp->len - (sep + 1 - resp->data) != APR_MD5_DIGESTSIZE * 2 || !hex_decode(cdigest, sep + 1)) return fail(conn, pool, "Malformed client response in authentication"); *user = apr_pstrmemdup(pool, resp->data, sep - resp->data); /* Verify the digest against the password in pwfile. */ svn_config_get(pwdb, &password, SVN_CONFIG_SECTION_USERS, *user, NULL); if (!password) return fail(conn, pool, "Username not found"); compute_digest(sdigest, challenge, password); if (memcmp(cdigest, sdigest, sizeof(sdigest)) != 0) return fail(conn, pool, "Password incorrect"); *success = TRUE; return svn_ra_svn__write_tuple(conn, pool, "w()", "success"); }
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->client_info->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); /* sasl uses unsigned int for the length of strings, we use apr_size_t * which may not be the same size. Deal with potential integer overflow */ if (in && in->len > UINT_MAX) return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("Initial token is too long")); result = svn_sasl__server_start(sasl_ctx, mech, in ? in->data : NULL, in ? (unsigned int) 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); if (in->len > UINT_MAX) return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("Step response is too long")); result = svn_sasl__server_step(sasl_ctx, in->data, (unsigned int) 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; }