mech-rpa.c revision 648d24583c1574441c4fa0331a90bd4d6e7996c5
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 const unsigned char *service_ucs2be;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen const unsigned char *username_ucs2be;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen const unsigned char *realm_ucs2be;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* requested: */
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* received: */
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned char *user_challenge;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned char *user_response;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen/* Object id encoded using ASN.1 DER */
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenstatic const unsigned char rpa_oid[] = {
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,
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,
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 unsigned int len = 0;
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;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned int len;
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainenrpa_verify_realm(struct rpa_auth_request *request, const char *realm)
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen const char *const *tmp;
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen tmp = request->auth_request.auth->auth_realms;
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen default_realm = request->auth_request.auth->default_realm != NULL ?
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 unsigned int len;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Read username@realm */
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen if ((realm == NULL) || !rpa_verify_realm(request, realm + 1)) {
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)
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen struct auth *auth = request->auth_request.auth;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned char timestamp[RPA_TIMESTAMP_LEN / 2];
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen const char *const *tmp;
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen for (tmp = auth->auth_realms; *tmp != NULL; tmp++) {
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen rpa_add_realm(realms, *tmp, request->auth_request.service);
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen rpa_add_realm(realms, auth->default_realm != NULL ?
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)
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen auth_request_log_error(&request->auth_request, "rpa",
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen "invalid credentials length");
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen memcpy(request->pwd_md5, credentials, sizeof(request->pwd_md5));
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen return memcmp(response, request->user_response, sizeof(response)) == 0;
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainenrpa_credentials_callback(enum passdb_result result,
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen const unsigned char *credentials, size_t size,
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen const unsigned char *token4;
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen if (!verify_credentials(request, credentials, size))
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainen token4 = mech_rpa_build_token4(request, &token4_size);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenmech_rpa_auth_phase1(struct auth_request *auth_request,
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen const unsigned char *token2;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen if (!rpa_parse_token1(data, data_size, &error)) {
5bcbe269efd7aeb8bf38a16d1e7cdaee1425576bTimo Sirainen token2 = mech_rpa_build_token2(request, &token2_size);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->service_ucs2be = ucs2be_str(request->pool, service,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE,
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenmech_rpa_auth_phase2(struct auth_request *auth_request,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen if (!rpa_parse_token3(request, data, data_size, &error)) {
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)) {
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)
cbf9ab418bb8f5fb41b15fad82b47b9cd6ee8a6bTimo Sirainen safe_memset(request->pwd_md5, 0, sizeof(request->pwd_md5));
4ac5448461b63de9637de839fbc611a3d503287cTimo Sirainenstatic struct auth_request *mech_rpa_auth_new(void)
e03ec0b7b9d92551331bc509bcd86920544171d1Timo Sirainen pool = pool_alloconly_create("rpa_auth_request", 1024);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request = p_new(pool, struct rpa_auth_request, 1);