mech-rpa.c revision 87cc5e9025e7fb6408f0de64c48d2d2897773ba5
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen * Compuserve RPA authentication mechanism.
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen * This program is free software; you can redistribute it and/or modify
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen * it under the terms of the GNU Lesser General Public License as published
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen * by the Free Software Foundation; either version 2 of the License, or
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen * (at your option) any later version.
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* cached: */
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned char *pwd_md5;
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 unsigned char *session_key;
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 *digest)
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned char z[48];
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen memset(z, 0, sizeof(z));
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);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen * Compute server -> client authentication response.
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenstatic void rpa_server_response(struct rpa_auth_request *request,
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned char *digest)
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned char z[48];
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen memset(z, 0, sizeof(z));
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);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen for (i = 0; i < 16; i++)
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);
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)
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen const unsigned char *end = ((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;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenrpa_parse_username(pool_t pool, const char *username)
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenrpa_parse_token3(struct rpa_auth_request *request, const void *data,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen struct auth_request *auth_request = &request->auth_request;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen const unsigned char *end = ((unsigned char *)data) + data_size;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen const unsigned char *p;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned int len;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Read username@realm */
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen auth_request->user = rpa_parse_username(request->pool, user);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->username_ucs2be = ucs2be_str(request->pool, auth_request->user,
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)
63969c244e8973a61760a98a23b127827d3d652cTimo Sirainenstatic const unsigned char *
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenmech_rpa_build_token2(struct rpa_auth_request *request,
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned int length;
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned char timestamp[RPA_TIMESTAMP_LEN / 2];
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 */
63969c244e8973a61760a98a23b127827d3d652cTimo Sirainenstatic const unsigned char *
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenmech_rpa_build_token4(struct rpa_auth_request *request, size_t *size)
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen unsigned int length = sizeof(rpa_oid) + 17 + 17 + 1;
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen buf = buffer_create_dynamic(request->pool, length + 4);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Generate random session key */
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->session_key = p_malloc(request->pool, 16);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Server authentication response */
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen rpa_server_response(request, server_response);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen /* Status, 0 - success */
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenrpa_credentials_callback(const char *credentials,
defc2cef0658ea5abe145111336ecc2274eecdb8Timo Sirainen const unsigned char *token4;
defc2cef0658ea5abe145111336ecc2274eecdb8Timo Sirainen mech_auth_finish(auth_request, NULL, 0, FALSE);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->pwd_md5 = p_malloc(request->pool, 16);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen hash_buffer = buffer_create_data(request->pool, request->pwd_md5, 16);
defc2cef0658ea5abe145111336ecc2274eecdb8Timo Sirainen if (memcmp(response, request->user_response, 16) != 0) {
defc2cef0658ea5abe145111336ecc2274eecdb8Timo Sirainen mech_auth_finish(auth_request, NULL, 0, FALSE);
defc2cef0658ea5abe145111336ecc2274eecdb8Timo Sirainen token4 = mech_rpa_build_token4(request, &token4_size);
defc2cef0658ea5abe145111336ecc2274eecdb8Timo Sirainen auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE,
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)) {
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen mech_auth_finish(auth_request, NULL, 0, FALSE);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen token2 = mech_rpa_build_token2(request, t_strconcat(service, "@",
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->service_ucs2be = ucs2be_str(request->pool, service,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->realm_ucs2be = ucs2be_str(request->pool, my_hostname,
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)) {
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen mech_auth_finish(auth_request, NULL, 0, FALSE);
9abfe876fa81576f130f3f82f622ae936c21a716Timo Sirainen if (!mech_fix_username(auth_request->user, &error)) {
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen mech_auth_finish(auth_request, NULL, 0, FALSE);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_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)) {
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen i_info("rpa(%s): 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);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen mech_auth_finish(auth_request, NULL, 0, FALSE);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenmech_rpa_auth_initial(struct auth_request *auth_request,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE, NULL, 0);
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenmech_rpa_auth_free(struct auth_request *auth_request)
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenstatic struct auth_request *mech_rpa_auth_new(void)
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainen pool = pool_alloconly_create("rpa_auth_request", 256);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request = p_new(pool, struct rpa_auth_request, 1);