54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen/*
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen * APOP (RFC-1460) authentication mechanism.
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen *
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen *
e074ffeaee1ce283bd42f167c6810e3d013f8218Timo Sirainen * This software is released under the MIT license.
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen */
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "auth-common.h"
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen#include "mech.h"
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen#include "passdb.h"
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen#include "md5.h"
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen#include "buffer.h"
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen#include "auth-client-connection.h"
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen#include "auth-master-connection.h"
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
9eedd38c2985596fb692dcca62193379d34c4c5bTimo Sirainen#include <stdio.h>
9eedd38c2985596fb692dcca62193379d34c4c5bTimo Sirainen#include <unistd.h>
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainenstruct apop_auth_request {
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen struct auth_request auth_request;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen pool_t pool;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen /* requested: */
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen char *challenge;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen /* received: */
5d8969b92762691fd3e326f05992e7d64fd42675Timo Sirainen unsigned char response_digest[16];
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen};
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenstatic bool verify_credentials(struct apop_auth_request *request,
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen const unsigned char *credentials, size_t size)
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen{
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen unsigned char digest[16];
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen struct md5_context ctx;
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen md5_init(&ctx);
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen md5_update(&ctx, request->challenge, strlen(request->challenge));
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen md5_update(&ctx, credentials, size);
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen md5_final(&ctx, digest);
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen
ace06232cfa0e99ecca1040e8553b3216d025768Timo Sirainen return mem_equals_timing_safe(digest, request->response_digest, 16);
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen}
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainenstatic void
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainenapop_credentials_callback(enum passdb_result result,
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen const unsigned char *credentials, size_t size,
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen struct auth_request *auth_request)
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen{
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen struct apop_auth_request *request =
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen (struct apop_auth_request *)auth_request;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen switch (result) {
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen case PASSDB_RESULT_OK:
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen if (verify_credentials(request, credentials, size))
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen auth_request_success(auth_request, "", 0);
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainen else
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainen auth_request_fail(auth_request);
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen break;
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen case PASSDB_RESULT_INTERNAL_FAILURE:
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen auth_request_internal_failure(auth_request);
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen break;
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen default:
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen auth_request_fail(auth_request);
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen break;
eb05765777cf968a7fb784602c310a8a418156a1Timo Sirainen }
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen}
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenstatic void
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainenmech_apop_auth_initial(struct auth_request *auth_request,
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen const unsigned char *data, size_t data_size)
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen{
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen struct apop_auth_request *request =
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen (struct apop_auth_request *)auth_request;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen const unsigned char *tmp, *end, *username = NULL;
9eedd38c2985596fb692dcca62193379d34c4c5bTimo Sirainen unsigned long pid, connect_uid, timestamp;
9eedd38c2985596fb692dcca62193379d34c4c5bTimo Sirainen const char *error;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
5d8969b92762691fd3e326f05992e7d64fd42675Timo Sirainen /* pop3-login handles sending the challenge and getting the response.
5d8969b92762691fd3e326f05992e7d64fd42675Timo Sirainen Our input here is: <challenge> \0 <username> \0 <response> */
5d8969b92762691fd3e326f05992e7d64fd42675Timo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen if (data_size == 0) {
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen /* Should never happen */
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_info(auth_request, AUTH_SUBSYS_MECH,
d47cf089bee2ea1f5a5a791eb5fc5e25884ad9d0Aki Tuomi "no initial response");
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen auth_request_fail(auth_request);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen return;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen }
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen tmp = data;
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen end = data + data_size;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
5d8969b92762691fd3e326f05992e7d64fd42675Timo Sirainen /* get the challenge */
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen while (tmp != end && *tmp != '\0')
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen tmp++;
5d8969b92762691fd3e326f05992e7d64fd42675Timo Sirainen request->challenge = p_strdup_until(request->pool, data, tmp);
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen if (tmp != end) {
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen /* get the username */
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen username = ++tmp;
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen while (tmp != end && *tmp != '\0')
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen tmp++;
4ad29d683940a58dbdbeb9ec6bb63536a61acd76Aki Tuomi } else {
4ad29d683940a58dbdbeb9ec6bb63536a61acd76Aki Tuomi /* should never happen */
4ad29d683940a58dbdbeb9ec6bb63536a61acd76Aki Tuomi auth_request_log_info(auth_request, AUTH_SUBSYS_MECH,
4ad29d683940a58dbdbeb9ec6bb63536a61acd76Aki Tuomi "malformed data");
4ad29d683940a58dbdbeb9ec6bb63536a61acd76Aki Tuomi auth_request_fail(auth_request);
4ad29d683940a58dbdbeb9ec6bb63536a61acd76Aki Tuomi return;
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen }
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen if (tmp + 1 + 16 != end) {
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen /* Should never happen */
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_info(auth_request, AUTH_SUBSYS_MECH,
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen "malformed data");
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen auth_request_fail(auth_request);
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen return;
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen }
2964c8ebf72349ce1848de142a57db484fa34280Timo Sirainen memcpy(request->response_digest, tmp + 1,
2964c8ebf72349ce1848de142a57db484fa34280Timo Sirainen sizeof(request->response_digest));
5e5c6dc798670afe6b717529e0df2c94879fd112Timo Sirainen
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen /* the challenge must begin with trusted unique ID. we trust only
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen ourself, so make sure it matches our connection specific UID
9eedd38c2985596fb692dcca62193379d34c4c5bTimo Sirainen which we told to client in handshake. Also require a timestamp
9eedd38c2985596fb692dcca62193379d34c4c5bTimo Sirainen which is later than this process's start time. */
9eedd38c2985596fb692dcca62193379d34c4c5bTimo Sirainen
5d8969b92762691fd3e326f05992e7d64fd42675Timo Sirainen if (sscanf(request->challenge, "<%lx.%lx.%lx.",
9eedd38c2985596fb692dcca62193379d34c4c5bTimo Sirainen &pid, &connect_uid, &timestamp) != 3 ||
657afb33796f8216c568ad813627da89970760beTimo Sirainen connect_uid != auth_request->connect_uid ||
9eedd38c2985596fb692dcca62193379d34c4c5bTimo Sirainen pid != (unsigned long)getpid() ||
9eedd38c2985596fb692dcca62193379d34c4c5bTimo Sirainen (time_t)timestamp < process_start_time) {
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_info(auth_request, AUTH_SUBSYS_MECH,
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen "invalid challenge");
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen auth_request_fail(auth_request);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen return;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen }
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen if (!auth_request_set_username(auth_request, (const char *)username,
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen &error)) {
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen auth_request_log_info(auth_request, AUTH_SUBSYS_MECH, "%s", error);
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen auth_request_fail(auth_request);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen return;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen }
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
a8e132559a7ebe54c8269d79ce29fa3338c76199Timo Sirainen auth_request_lookup_credentials(auth_request, "PLAIN",
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen apop_credentials_callback);
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen}
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
4ac5448461b63de9637de839fbc611a3d503287cTimo Sirainenstatic struct auth_request *mech_apop_auth_new(void)
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen{
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen struct apop_auth_request *request;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen pool_t pool;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
1b81b28b2e7856748cffd7d01052a944b6c80b23Timo Sirainen pool = pool_alloconly_create(MEMPOOL_GROWING"apop_auth_request", 2048);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request = p_new(pool, struct apop_auth_request, 1);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->pool = pool;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->auth_request.pool = pool;
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen return &request->auth_request;
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen}
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainenconst struct mech_module mech_apop = {
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen "APOP",
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .flags = MECH_SEC_PRIVATE | MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE,
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .passdb_need = MECH_PASSDB_NEED_VERIFY_RESPONSE,
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen mech_apop_auth_new,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen mech_apop_auth_initial,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen NULL,
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen mech_generic_auth_free
54bde50b7d791bd8c3d2a6581f8a4ff26d9f0834Timo Sirainen};