a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen * Compuserve RPA authentication mechanism.
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
e074ffeaee1ce283bd42f167c6810e3d013f8218Timo Sirainen * This software is released under the MIT license.
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* cached: */
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* requested: */
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* received: */
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen/* Object id encoded using ASN.1 DER */
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x73, 0x01, 0x01
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenvoid *ucs2be_str(pool_t pool, const char *str, size_t *size);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen * Compute client -> server authentication response.
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenstatic void rpa_user_response(struct rpa_auth_request *request,
9625595c47c665f5aee57ebfcb1fcbe9ad1bf3a0Martti Rannanjärvi unsigned char digest[STATIC_ARRAY MD5_RESULTLEN])
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned char z[48];
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen memset(z, 0, sizeof(z));
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen md5_update(&ctx, request->pwd_md5, sizeof(request->pwd_md5));
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->username_ucs2be, request->username_len);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->service_ucs2be, request->service_len);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->realm_ucs2be, request->realm_len);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->user_challenge, request->user_challenge_len);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->service_challenge, RPA_SCHALLENGE_LEN);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->service_timestamp, RPA_TIMESTAMP_LEN);
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen md5_update(&ctx, request->pwd_md5, sizeof(request->pwd_md5));
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen * Compute server -> client authentication response.
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenstatic void rpa_server_response(struct rpa_auth_request *request,
9625595c47c665f5aee57ebfcb1fcbe9ad1bf3a0Martti Rannanjärvi unsigned char digest[STATIC_ARRAY MD5_RESULTLEN])
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned char z[48];
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen unsigned int i;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen memset(z, 0, sizeof(z));
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen md5_update(&ctx, request->pwd_md5, sizeof(request->pwd_md5));
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->service_ucs2be, request->service_len);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->username_ucs2be, request->username_len);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->realm_ucs2be, request->realm_len);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->service_challenge, RPA_SCHALLENGE_LEN);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->user_challenge, request->user_challenge_len);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->service_timestamp, RPA_TIMESTAMP_LEN);
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen md5_update(&ctx, request->pwd_md5, sizeof(request->pwd_md5));
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen for (i = 0; i < sizeof(tmp); i++)
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen md5_update(&ctx, request->pwd_md5, sizeof(request->pwd_md5));
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->service_ucs2be, request->service_len);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->username_ucs2be, request->username_len);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->realm_ucs2be, request->realm_len);
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen md5_update(&ctx, request->session_key, sizeof(request->session_key));
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->service_challenge, RPA_SCHALLENGE_LEN);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->user_challenge, request->user_challenge_len);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen md5_update(&ctx, request->service_timestamp, RPA_TIMESTAMP_LEN);
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen md5_update(&ctx, request->pwd_md5, sizeof(request->pwd_md5));
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenstatic const unsigned char *
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenrpa_check_message(const unsigned char *data, const unsigned char *end,
9abfe876fa81576f130f3f82f622ae936c21a716Timo Sirainen const char **error)
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen const unsigned char *p = data;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen if ((*p & 0x80) != 0) {
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen while (nbytes-- > 0) {
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen *error = "structure length disagrees with data size";
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen if (memcmp(p, rpa_oid, sizeof(rpa_oid)) != 0) {
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen return p + sizeof(rpa_oid);
9abfe876fa81576f130f3f82f622ae936c21a716Timo Sirainenrpa_parse_token1(const void *data, size_t data_size, const char **error)
8d6cb44a0161d88743756733f83c4fb278485987Timo Sirainen const unsigned char *end = ((const unsigned char *) data) + data_size;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen const unsigned char *p;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenstatic unsigned int
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenrpa_read_buffer(pool_t pool, const unsigned char **data,
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen const unsigned char *end, unsigned char **buffer)
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen const unsigned char *p = *data;
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenrpa_parse_token3(struct rpa_auth_request *request, const void *data,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen struct auth_request *auth_request = &request->auth_request;
8d6cb44a0161d88743756733f83c4fb278485987Timo Sirainen const unsigned char *end = ((const unsigned char *)data) + data_size;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen const unsigned char *p;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Read username@realm */
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen if (!auth_request_set_username(auth_request, user, error))
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->username_ucs2be = ucs2be_str(request->pool, auth_request->user,
ac3cf88b470320c924965622329930c2c89b8e72Timo Sirainen request->realm_ucs2be = ucs2be_str(request->pool, realm,
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Read user challenge */
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->user_challenge_len = rpa_read_buffer(request->pool, &p, end,
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Read user response */
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen len = rpa_read_buffer(request->pool, &p, end, &request->user_response);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenbuffer_append_asn1_length(buffer_t *buf, unsigned int length)
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainenrpa_add_realm(string_t *realms, const char *realm, const char *service)
63969c244e8973a61760a98a23b127827d3d652cTimo Sirainenstatic const unsigned char *
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainenmech_rpa_build_token2(struct rpa_auth_request *request, size_t *size)
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen const struct auth_settings *set = request->auth_request.set;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned char timestamp[RPA_TIMESTAMP_LEN / 2];
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen for (tmp = set->realms_arr; *tmp != NULL; tmp++) {
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen rpa_add_realm(realms, *tmp, request->auth_request.service);
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen rpa_add_realm(realms, *set->default_realm != '\0' ?
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen length = sizeof(rpa_oid) + 3 + RPA_SCHALLENGE_LEN +
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen buf = buffer_create_dynamic(request->pool, length + 4);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Protocol version */
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Service challenge */
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen random_fill(request->service_challenge, RPA_SCHALLENGE_LEN);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen buffer_append(buf, request->service_challenge, RPA_SCHALLENGE_LEN);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Timestamp, looks like clients accept anything we send */
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->service_timestamp = p_malloc(request->pool, RPA_TIMESTAMP_LEN);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen buffer_append(buf, request->service_timestamp, RPA_TIMESTAMP_LEN);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Realm list */
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen buffer_append(buf, str_c(realms), realms_len);
63969c244e8973a61760a98a23b127827d3d652cTimo Sirainenstatic const unsigned char *
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenmech_rpa_build_token4(struct rpa_auth_request *request, size_t *size)
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen buf = buffer_create_dynamic(request->pool, length + 4);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Generate random session key */
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen random_fill(request->session_key, sizeof(request->session_key));
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Server authentication response */
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen rpa_server_response(request, server_response);
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen buffer_append_c(buf, sizeof(server_response));
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen buffer_append(buf, server_response, sizeof(server_response));
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen buffer_append_c(buf, sizeof(request->session_key));
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen buffer_append(buf, request->session_key, sizeof(request->session_key));
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Status, 0 - success */
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenstatic bool verify_credentials(struct rpa_auth_request *request,
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen const unsigned char *credentials, size_t size)
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_error(&request->auth_request, AUTH_SUBSYS_MECH,
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen "invalid credentials length");
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen memcpy(request->pwd_md5, credentials, sizeof(request->pwd_md5));
ace06232cfa0e99ecca1040e8553b3216d025768Timo Sirainen return mem_equals_timing_safe(response, request->user_response, sizeof(response));
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainenrpa_credentials_callback(enum passdb_result result,
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen const unsigned char *credentials, size_t size,
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen if (!verify_credentials(request, credentials, size))
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainen token4 = mech_rpa_build_token4(request, &token4_size);
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen auth_request_handler_reply_continue(auth_request,
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenmech_rpa_auth_phase1(struct auth_request *auth_request,
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen if (!rpa_parse_token1(data, data_size, &error)) {
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_info(auth_request, AUTH_SUBSYS_MECH,
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen token2 = mech_rpa_build_token2(request, &token2_size);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->service_ucs2be = ucs2be_str(request->pool, service,
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen auth_request_handler_reply_continue(auth_request, token2, token2_size);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenmech_rpa_auth_phase2(struct auth_request *auth_request,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen if (!rpa_parse_token3(request, data, data_size, &error)) {
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_info(auth_request, AUTH_SUBSYS_MECH,
a8e132559a7ebe54c8269d79ce29fa3338c76199Timo Sirainen auth_request_lookup_credentials(auth_request, "RPA",
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenmech_rpa_auth_phase3(struct auth_request *auth_request,
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen static const unsigned char client_ack[3] = { 0x60, 0x01, 0x00 };
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen (memcmp(data, client_ack, sizeof(client_ack)) != 0)) {
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_info(auth_request, AUTH_SUBSYS_MECH,
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen "invalid token 5 or client rejects us");
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenmech_rpa_auth_continue(struct auth_request *auth_request,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen mech_rpa_auth_phase1(auth_request, data, data_size);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen mech_rpa_auth_phase2(auth_request, data, data_size);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen mech_rpa_auth_phase3(auth_request, data, data_size);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenmech_rpa_auth_free(struct auth_request *auth_request)
a8672a757f6f9a610d9bf1e65bd3503904f44f1fTimo Sirainen safe_memset(request->pwd_md5, 0, sizeof(request->pwd_md5));
4ac5448461b63de9637de839fbc611a3d503287cTimo Sirainenstatic struct auth_request *mech_rpa_auth_new(void)
1b81b28b2e7856748cffd7d01052a944b6c80b23Timo Sirainen pool = pool_alloconly_create(MEMPOOL_GROWING"rpa_auth_request", 2048);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request = p_new(pool, struct rpa_auth_request, 1);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .flags = MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE |