#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <freeradius-devel/md5.h>
#include <ctype.h>
#define SM_AUTHTYPE ((11406<<16)|101)
#define SM_CHALLENGE ((11406<<16)|102)
#define SM_RESPONSE ((11406<<16)|103)
static void calc_apop_digest(char * buffer, const char * challenge, int challen, const char * password){
FR_MD5_CTX Context;
fr_MD5Init(&Context);
fr_MD5Update(&Context,challenge,challen);
fr_MD5Update(&Context,password,strlen(password));
fr_MD5Final(buffer,&Context);
}
static void calc_md5_digest(char * buffer, const char * challenge, int challen, const char * password){
char 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_MD5Init(&Context);
fr_MD5Update(&Context,buf,64+challen);
memset(buf, 0x5c, 64);
for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
fr_MD5Final(buf+64,&Context);
fr_MD5Init(&Context);
fr_MD5Update(&Context,buf,64+16);
fr_MD5Final(buffer,&Context);
}
static void calc_md4_digest(char * buffer, const char * challenge, int challen, const char * password){
char 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_MD4Init(&Context);
fr_MD4Update(&Context,buf,64+challen);
memset(buf, 0x5c, 64);
for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
fr_MD4Final(buf+64,&Context);
fr_MD4Init(&Context);
fr_MD4Update(&Context,buf,64+16);
fr_MD4Final(buffer,&Context);
}
static void calc_sha1_digest(char * buffer, const char * challenge, int challen, const char * password){
char 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_SHA1Init(&Context);
fr_SHA1Update(&Context,buf,64+challen);
memset(buf, 0x5c, 64);
for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
fr_SHA1Final(buf+64,&Context);
fr_SHA1Init(&Context);
fr_SHA1Update(&Context,buf,64+20);
fr_SHA1Final(buffer,&Context);
}
static int cram_authenticate(UNUSED void * instance, REQUEST *request)
{
VALUE_PAIR *authtype, *challenge, *response, *password;
char buffer[64];
password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD);
if(!password) {
radlog(L_AUTH, "rlm_cram: Cleartext-Password is required for authentication.");
return RLM_MODULE_INVALID;
}
authtype = pairfind(request->packet->vps, SM_AUTHTYPE);
if(!authtype) {
radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Authtype missed");
return RLM_MODULE_INVALID;
}
challenge = pairfind(request->packet->vps, SM_CHALLENGE);
if(!challenge) {
radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Challenge missed");
return RLM_MODULE_INVALID;
}
response = pairfind(request->packet->vps, SM_RESPONSE);
if(!response) {
radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Response missed");
return RLM_MODULE_INVALID;
}
switch(authtype->vp_integer){
case 2:
if(challenge->length < 5 || response->length != 16) {
radlog(L_AUTH, "rlm_cram: invalid MD5 challenge/response length");
return RLM_MODULE_INVALID;
}
calc_md5_digest(buffer, challenge->vp_strvalue, challenge->length, password->vp_strvalue);
if(!memcmp(buffer, response->vp_strvalue, 16)) return RLM_MODULE_OK;
break;
case 3:
if(challenge->length < 5 || response->length != 16) {
radlog(L_AUTH, "rlm_cram: invalid APOP challenge/response length");
return RLM_MODULE_INVALID;
}
calc_apop_digest(buffer, challenge->vp_strvalue, challenge->length, password->vp_strvalue);
if(!memcmp(buffer, response->vp_strvalue, 16)) return RLM_MODULE_OK;
break;
case 8:
if(challenge->length < 5 || response->length != 16) {
radlog(L_AUTH, "rlm_cram: invalid MD4 challenge/response length");
return RLM_MODULE_INVALID;
}
calc_md4_digest(buffer, challenge->vp_strvalue, challenge->length, password->vp_strvalue);
if(!memcmp(buffer, response->vp_strvalue, 16)) return RLM_MODULE_OK;
break;
case 9:
if(challenge->length < 5 || response->length != 20) {
radlog(L_AUTH, "rlm_cram: invalid MD4 challenge/response length");
return RLM_MODULE_INVALID;
}
calc_sha1_digest(buffer, challenge->vp_strvalue, challenge->length, password->vp_strvalue);
if(!memcmp(buffer, response->vp_strvalue, 20)) return RLM_MODULE_OK;
break;
default:
radlog(L_AUTH, "rlm_cram: unsupported Sandy-Mail-Authtype");
return RLM_MODULE_INVALID;
}
return RLM_MODULE_NOTFOUND;
}
module_t rlm_cram = {
RLM_MODULE_INIT,
"CRAM",
RLM_TYPE_THREAD_SAFE,
NULL,
NULL,
{
cram_authenticate,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
},
};