/* * This program is is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ /** * $Id: c7a667ac2baa7482b520627f8f3974a53a042eb3 $ * @file rlm_cram.c * @brief CRAM mail authentication (APOP, CRAM-MD5) @verbatim Attributes used (Vendor Code/PEN: 11406, you may change it to your own) 101 (Sandy-Mail-Authtype), selects CRAM protocol, possible values: 2: CRAM-MD5 3: APOP 8: CRAM-MD4 9: CRAM-SHA1 102 (Sandy-Mail-Challenge), contains server's challenge (usually text banner) 103 (Sandy-Mail-Response), contains client's response, 16 octets for APOP/CRAM-MD5/CRAM-MD4, 20 octets for CRAM-SHA1 @endverbatim * @copyright 2001,2006 The FreeRADIUS server project * @copyright 2002 SANDY (http://www.sandy.ru/) under GPLr */ RCSID("$Id: c7a667ac2baa7482b520627f8f3974a53a042eb3 $") #include #include #include #include #define VENDORPEC_SM 11406 #define SM_AUTHTYPE 101 #define SM_CHALLENGE 102 #define SM_RESPONSE 103 static void calc_apop_digest(uint8_t *buffer, uint8_t const *challenge, size_t challen, char const *password) { FR_MD5_CTX context; fr_md5_init(&context); fr_md5_update(&context, challenge, challen); fr_md5_update(&context, (uint8_t const *) password, strlen(password)); fr_md5_final(buffer, &context); } static void calc_md5_digest(uint8_t *buffer, uint8_t const *challenge, size_t challen, char const *password) { uint8_t buf[1024]; int i; FR_MD5_CTX context; memset(buf, 0, 1024); memset(buf, 0x36, 64); for(i=0; i<64 && password[i]; i++) buf[i]^=password[i]; memcpy(buf+64, challenge, challen); fr_md5_init(&context); fr_md5_update(&context, buf, 64+challen); memset(buf, 0x5c, 64); for(i=0; i<64 && password[i]; i++) buf[i]^=password[i]; fr_md5_final(buf+64,&context); fr_md5_init(&context); fr_md5_update(&context,buf,64+16); fr_md5_final(buffer,&context); } static void calc_md4_digest(uint8_t *buffer, uint8_t const *challenge, size_t challen, char const *password) { uint8_t buf[1024]; int i; FR_MD4_CTX context; memset(buf, 0, 1024); memset(buf, 0x36, 64); for(i=0; i<64 && password[i]; i++) buf[i]^=password[i]; memcpy(buf+64, challenge, challen); fr_md4_init(&context); fr_md4_update(&context,buf,64+challen); memset(buf, 0x5c, 64); for(i=0; i<64 && password[i]; i++) buf[i]^=password[i]; fr_md4_final(buf+64,&context); fr_md4_init(&context); fr_md4_update(&context,buf,64+16); fr_md4_final(buffer,&context); } static void calc_sha1_digest(uint8_t *buffer, uint8_t const *challenge, size_t challen, char const *password) { uint8_t buf[1024]; int i; fr_sha1_ctx context; memset(buf, 0, 1024); memset(buf, 0x36, 64); for(i=0; i<64 && password[i]; i++) buf[i]^=password[i]; memcpy(buf+64, challenge, challen); fr_sha1_init(&context); fr_sha1_update(&context,buf,64+challen); memset(buf, 0x5c, 64); for(i=0; i<64 && password[i]; i++) buf[i]^=password[i]; fr_sha1_final(buf+64,&context); fr_sha1_init(&context); fr_sha1_update(&context,buf,64+20); fr_sha1_final(buffer,&context); } static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void * instance, REQUEST *request) { VALUE_PAIR *authtype, *challenge, *response, *password; uint8_t buffer[64]; password = fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY); if(!password) { AUTH("rlm_cram: Cleartext-Password is required for authentication"); return RLM_MODULE_INVALID; } authtype = fr_pair_find_by_num(request->packet->vps, SM_AUTHTYPE, VENDORPEC_SM, TAG_ANY); if(!authtype) { AUTH("rlm_cram: Required attribute Sandy-Mail-Authtype missed"); return RLM_MODULE_INVALID; } challenge = fr_pair_find_by_num(request->packet->vps, SM_CHALLENGE, VENDORPEC_SM, TAG_ANY); if(!challenge) { AUTH("rlm_cram: Required attribute Sandy-Mail-Challenge missed"); return RLM_MODULE_INVALID; } response = fr_pair_find_by_num(request->packet->vps, SM_RESPONSE, VENDORPEC_SM, TAG_ANY); if(!response) { AUTH("rlm_cram: Required attribute Sandy-Mail-Response missed"); return RLM_MODULE_INVALID; } switch (authtype->vp_integer){ case 2: /* CRAM-MD5 */ if(challenge->vp_length < 5 || response->vp_length != 16) { AUTH("rlm_cram: invalid MD5 challenge/response length"); return RLM_MODULE_INVALID; } calc_md5_digest(buffer, challenge->vp_octets, challenge->vp_length, password->vp_strvalue); if(!memcmp(buffer, response->vp_octets, 16)) return RLM_MODULE_OK; break; case 3: /* APOP */ if(challenge->vp_length < 5 || response->vp_length != 16) { AUTH("rlm_cram: invalid APOP challenge/response length"); return RLM_MODULE_INVALID; } calc_apop_digest(buffer, challenge->vp_octets, challenge->vp_length, password->vp_strvalue); if(!memcmp(buffer, response->vp_octets, 16)) return RLM_MODULE_OK; break; case 8: /* CRAM-MD4 */ if(challenge->vp_length < 5 || response->vp_length != 16) { AUTH("rlm_cram: invalid MD4 challenge/response length"); return RLM_MODULE_INVALID; } calc_md4_digest(buffer, challenge->vp_octets, challenge->vp_length, password->vp_strvalue); if(!memcmp(buffer, response->vp_octets, 16)) return RLM_MODULE_OK; break; case 9: /* CRAM-SHA1 */ if(challenge->vp_length < 5 || response->vp_length != 20) { AUTH("rlm_cram: invalid MD4 challenge/response length"); return RLM_MODULE_INVALID; } calc_sha1_digest(buffer, challenge->vp_octets, challenge->vp_length, password->vp_strvalue); if(!memcmp(buffer, response->vp_octets, 20)) return RLM_MODULE_OK; break; default: AUTH("rlm_cram: unsupported Sandy-Mail-Authtype"); return RLM_MODULE_INVALID; } return RLM_MODULE_NOTFOUND; } extern module_t rlm_cram; module_t rlm_cram = { .magic = RLM_MODULE_INIT, .name = "cram", .type = RLM_TYPE_THREAD_SAFE, .methods = { [MOD_AUTHENTICATE] = mod_authenticate }, };