#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <freeradius-devel/rad_assert.h>
#include <freeradius-devel/md5.h>
#include <freeradius-devel/sha1.h>
#include <ctype.h>
#include "mschap.h"
#include "smbdes.h"
#ifdef __APPLE__
extern int do_od_mschap(REQUEST* request, VALUE_PAIR* response, VALUE_PAIR* challenge, const char* username_string);
#endif
#define ACB_DISABLED 0x0001
#define ACB_HOMDIRREQ 0x0002
#define ACB_PWNOTREQ 0x0004
#define ACB_TEMPDUP 0x0008
#define ACB_NORMAL 0x0010
#define ACB_MNS 0x0020
#define ACB_DOMTRUST 0x0040
#define ACB_WSTRUST 0x0080
#define ACB_SVRTRUST 0x0100
#define ACB_PWNOEXP 0x0200
#define ACB_AUTOLOCK 0x0400
static int pdb_decode_acct_ctrl(const char *p)
{
int acct_ctrl = 0;
int finished = 0;
if (*p != '[') return 0;
for (p++; *p && !finished; p++) {
switch (*p) {
case 'N':
acct_ctrl |= ACB_PWNOTREQ;
break;
case 'D':
acct_ctrl |= ACB_DISABLED ;
break;
case 'H':
acct_ctrl |= ACB_HOMDIRREQ;
break;
case 'T':
acct_ctrl |= ACB_TEMPDUP;
break;
case 'U':
acct_ctrl |= ACB_NORMAL;
break;
case 'M':
acct_ctrl |= ACB_MNS;
break;
case 'W':
acct_ctrl |= ACB_WSTRUST;
break;
case 'S':
acct_ctrl |= ACB_SVRTRUST;
break;
case 'L':
acct_ctrl |= ACB_AUTOLOCK;
break;
case 'X':
acct_ctrl |= ACB_PWNOEXP;
break;
case 'I':
acct_ctrl |= ACB_DOMTRUST;
break;
case ' ':
break;
case ':':
case '\n':
case '\0':
case ']':
default:
finished = 1;
break;
}
}
return acct_ctrl;
}
typedef struct rlm_mschap_t {
int use_mppe;
int require_encryption;
int require_strong;
int with_ntdomain_hack;
char *passwd_file;
const char *xlat_name;
char *ntlm_auth;
const char *auth_type;
int allow_retry;
char *retry_msg;
#ifdef __APPLE__
int open_directory;
#endif
} rlm_mschap_t;
static size_t mschap_xlat(void *instance, REQUEST *request,
char *fmt, char *out, size_t outlen,
RADIUS_ESCAPE_STRING func)
{
size_t i, data_len;
uint8_t *data = NULL;
uint8_t buffer[32];
VALUE_PAIR *user_name;
VALUE_PAIR *chap_challenge, *response;
rlm_mschap_t *inst = instance;
response = NULL;
func = func;
if (strncasecmp(fmt, "Challenge", 9) == 0) {
chap_challenge = pairfind(request->packet->vps,
PW_MSCHAP_CHALLENGE);
if (!chap_challenge) {
RDEBUG2("No MS-CHAP-Challenge in the request.");
return 0;
}
if (chap_challenge->length == 8) {
RDEBUG2(" mschap1: %02x",
chap_challenge->vp_octets[0]);
data = chap_challenge->vp_octets;
data_len = 8;
} else if (chap_challenge->length == 16) {
VALUE_PAIR *name_attr, *response_name;
char *username_string;
response = pairfind(request->packet->vps,
PW_MSCHAP2_RESPONSE);
if (!response) {
RDEBUG2("MS-CHAP2-Response is required to calculate MS-CHAPv1 challenge.");
return 0;
}
if (response->length < 50) {
radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format.");
return 0;
}
user_name = pairfind(request->packet->vps,
PW_USER_NAME);
if (!user_name) {
RDEBUG2("User-Name is required to calculate MS-CHAPv1 Challenge.");
return 0;
}
response_name = pairfind(request->packet->vps, PW_MS_CHAP_USER_NAME);
if (response_name) {
name_attr = response_name;
} else {
name_attr = user_name;
}
if ((username_string = strchr(name_attr->vp_strvalue, '\\')) != NULL) {
if (inst->with_ntdomain_hack) {
username_string++;
} else {
RDEBUG2("NT Domain delimeter found, should we have enabled with_ntdomain_hack?");
username_string = name_attr->vp_strvalue;
}
} else {
username_string = name_attr->vp_strvalue;
}
if (response_name &&
((user_name->length != response_name->length) ||
(strncasecmp(user_name->vp_strvalue, response_name->vp_strvalue, user_name->length) != 0))) {
RDEBUG("WARNING: User-Name (%s) is not the same as MS-CHAP Name (%s) from EAP-MSCHAPv2", user_name->vp_strvalue, response_name->vp_strvalue);
}
RDEBUG2("Creating challenge hash with username: %s",
username_string);
mschap_challenge_hash(response->vp_octets + 2,
chap_challenge->vp_octets,
username_string, buffer);
data = buffer;
data_len = 8;
} else {
RDEBUG2("Invalid MS-CHAP challenge length");
return 0;
}
} else if (strncasecmp(fmt, "NT-Response", 11) == 0) {
response = pairfind(request->packet->vps,
PW_MSCHAP_RESPONSE);
if (!response) response = pairfind(request->packet->vps,
PW_MSCHAP2_RESPONSE);
if (!response) {
RDEBUG2("No MS-CHAP-Response or MS-CHAP2-Response was found in the request.");
return 0;
}
if ((response->attribute == PW_MSCHAP_RESPONSE) &&
((response->vp_octets[1] & 0x01) == 0)) {
RDEBUG2("No NT-Response in MS-CHAP-Response");
return 0;
}
data = response->vp_octets + 26;
data_len = 24;
} else if (strncasecmp(fmt, "LM-Response", 11) == 0) {
response = pairfind(request->packet->vps,
PW_MSCHAP_RESPONSE);
if (!response) {
RDEBUG2("No MS-CHAP-Response was found in the request.");
return 0;
}
if ((response->vp_octets[1] & 0x01) != 0) {
RDEBUG2("No LM-Response in MS-CHAP-Response");
return 0;
}
data = response->vp_octets + 2;
data_len = 24;
} else if (strncasecmp(fmt, "NT-Domain", 9) == 0) {
char *p, *q;
user_name = pairfind(request->packet->vps, PW_USER_NAME);
if (!user_name) {
RDEBUG2("No User-Name was found in the request.");
return 0;
}
if (strncmp(user_name->vp_strvalue, "host/", 5) == 0) {
p = strchr(user_name->vp_strvalue, '.');
if (!p) {
RDEBUG2("setting NT-Domain to same as machine name");
strlcpy(out, user_name->vp_strvalue + 5, outlen);
} else {
p++;
q = strchr(p, '.');
if (q) *q = '\0';
strlcpy(out, p, outlen);
if (q) *q = '.';
}
} else {
p = strchr(user_name->vp_strvalue, '\\');
if (!p) {
RDEBUG2("No NT-Domain was found in the User-Name.");
return 0;
}
*p = '\0';
strlcpy(out, user_name->vp_strvalue, outlen);
*p = '\\';
}
return strlen(out);
} else if (strncasecmp(fmt, "User-Name", 9) == 0) {
char *p;
user_name = pairfind(request->packet->vps, PW_USER_NAME);
if (!user_name) {
RDEBUG2("No User-Name was found in the request.");
return 0;
}
if (strncmp(user_name->vp_strvalue, "host/", 5) == 0) {
p = strchr(user_name->vp_strvalue, '.');
if (p) *p = '\0';
snprintf(out, outlen, "%s$", user_name->vp_strvalue + 5);
if (p) *p = '.';
} else {
p = strchr(user_name->vp_strvalue, '\\');
if (p) {
p++;
} else {
p = user_name->vp_strvalue;
}
strlcpy(out, p, outlen);
}
return strlen(out);
} else if (strncasecmp(fmt, "NT-Hash ", 8) == 0) {
char *p;
char buf2[1024];
p = fmt + 8;
if ((p == '\0') || (outlen <= 32))
return 0;
while (isspace(*p)) p++;
if (!radius_xlat(buf2, sizeof(buf2),p,request,NULL)) {
RDEBUG("xlat failed");
*buffer = '\0';
return 0;
}
mschap_ntpwdhash(buffer,buf2);
fr_bin2hex(buffer, out, 16);
out[32] = '\0';
RDEBUG("NT-Hash of %s = %s", buf2, out);
return 32;
} else if (strncasecmp(fmt, "LM-Hash ", 8) == 0) {
char *p;
char buf2[1024];
p = fmt + 8;
if ((p == '\0') || (outlen <= 32))
return 0;
while (isspace(*p)) p++;
if (!radius_xlat(buf2, sizeof(buf2),p,request,NULL)) {
RDEBUG("xlat failed");
*buffer = '\0';
return 0;
}
smbdes_lmpwdhash(buf2, buffer);
fr_bin2hex(buffer, out, 16);
out[32] = '\0';
RDEBUG("LM-Hash of %s = %s", buf2, out);
return 32;
} else {
RDEBUG2("Unknown expansion string \"%s\"",
fmt);
return 0;
}
if (outlen == 0) return 0;
if (!data) {
RDEBUG2("Failed to do anything intelligent");
return 0;
}
if (outlen < ((data_len * 2) + 1)) {
data_len = (outlen - 1) / 2;
}
for (i = 0; i < data_len; i++) {
sprintf(out + (2 * i), "%02x", data[i]);
}
out[data_len * 2] = '\0';
return data_len * 2;
}
static const CONF_PARSER module_config[] = {
{ "use_mppe", PW_TYPE_BOOLEAN,
offsetof(rlm_mschap_t,use_mppe), NULL, "yes" },
{ "require_encryption", PW_TYPE_BOOLEAN,
offsetof(rlm_mschap_t,require_encryption), NULL, "no" },
{ "require_strong", PW_TYPE_BOOLEAN,
offsetof(rlm_mschap_t,require_strong), NULL, "no" },
{ "with_ntdomain_hack", PW_TYPE_BOOLEAN,
offsetof(rlm_mschap_t,with_ntdomain_hack), NULL, "no" },
{ "passwd", PW_TYPE_STRING_PTR,
offsetof(rlm_mschap_t, passwd_file), NULL, NULL },
{ "ntlm_auth", PW_TYPE_STRING_PTR,
offsetof(rlm_mschap_t, ntlm_auth), NULL, NULL },
{ "allow_retry", PW_TYPE_BOOLEAN,
offsetof(rlm_mschap_t, allow_retry), NULL, "yes" },
{ "retry_msg", PW_TYPE_STRING_PTR,
offsetof(rlm_mschap_t, retry_msg), NULL, NULL },
#ifdef __APPLE__
{ "use_open_directory", PW_TYPE_BOOLEAN,
offsetof(rlm_mschap_t,open_directory), NULL, "yes" },
#endif
{ NULL, -1, 0, NULL, NULL }
};
static int mschap_detach(void *instance){
#define inst ((rlm_mschap_t *)instance)
if (inst->xlat_name) {
xlat_unregister(inst->xlat_name, mschap_xlat);
free(inst->xlat_name);
}
free(instance);
return 0;
#undef inst
}
static int mschap_instantiate(CONF_SECTION *conf, void **instance)
{
rlm_mschap_t *inst;
inst = *instance = rad_malloc(sizeof(*inst));
if (!inst) {
return -1;
}
memset(inst, 0, sizeof(*inst));
if (cf_section_parse(conf, inst, module_config) < 0) {
free(inst);
return -1;
}
if (inst->passwd_file) {
radlog(L_ERR, "rlm_mschap: SMB password file is no longer supported in this module. Use rlm_passwd module instead");
mschap_detach(inst);
return -1;
}
inst->xlat_name = cf_section_name2(conf);
if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf);
inst->xlat_name = strdup(inst->xlat_name);
xlat_register(inst->xlat_name, mschap_xlat, inst);
if (!dict_valbyname(PW_AUTH_TYPE, inst->xlat_name)) {
inst->auth_type = "MS-CHAP";
} else {
inst->auth_type = inst->xlat_name;
}
return 0;
}
void mschap_add_reply(REQUEST *request, VALUE_PAIR** vp, unsigned char ident,
const char* name, const char* value, int len)
{
VALUE_PAIR *reply_attr;
reply_attr = pairmake(name, "", T_OP_EQ);
if (!reply_attr) {
RDEBUG("Failed to create attribute %s: %s\n", name, fr_strerror());
return;
}
reply_attr->vp_octets[0] = ident;
memcpy(reply_attr->vp_octets + 1, value, len);
reply_attr->length = len + 1;
pairadd(vp, reply_attr);
}
static void mppe_add_reply(REQUEST *request,
const char* name, const uint8_t * value, int len)
{
VALUE_PAIR *vp;
vp = radius_pairmake(request, &request->reply->vps, name, "", T_OP_EQ);
if (!vp) {
RDEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, fr_strerror());
return;
}
memcpy(vp->vp_octets, value, len);
vp->length = len;
}
static int do_mschap(rlm_mschap_t *inst,
REQUEST *request, VALUE_PAIR *password,
uint8_t *challenge, uint8_t *response,
uint8_t *nthashhash, int do_ntlm_auth)
{
uint8_t calculated[24];
if (!do_ntlm_auth) {
if (!password) {
RDEBUG2("FAILED: No NT/LM-Password. Cannot perform authentication.");
return -1;
}
smbdes_mschap(password->vp_strvalue, challenge, calculated);
if (rad_digest_cmp(response, calculated, 24) != 0) {
return -1;
}
if (password && (password->attribute == PW_NT_PASSWORD)) {
fr_md4_calc(nthashhash, password->vp_octets, 16);
} else {
memset(nthashhash, 0, 16);
}
} else {
int result;
char buffer[256];
memset(nthashhash, 0, 16);
result = radius_exec_program(inst->ntlm_auth, request,
TRUE,
buffer, sizeof(buffer),
NULL, NULL, 1);
if (result != 0) {
char *p;
VALUE_PAIR *vp = NULL;
RDEBUG2("External script failed.");
vp = pairmake("Module-Failure-Message", "", T_OP_EQ);
if (!vp) {
radlog_request(L_ERR, 0, request, "No memory to allocate Module-Failure-Message");
return RLM_MODULE_FAIL;
}
p = strchr(buffer, '\n');
if (p) *p = '\0';
snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue),
"%s: External script says %s",
inst->xlat_name, buffer);
vp->length = strlen(vp->vp_strvalue);
pairadd(&request->packet->vps, vp);
return -1;
}
if (memcmp(buffer, "NT_KEY: ", 8) != 0) {
RDEBUG2("Invalid output from ntlm_auth: expecting NT_KEY");
return -1;
}
if (strlen(buffer + 8) < 32) {
RDEBUG2("Invalid output from ntlm_auth: NT_KEY has unexpected length");
return -1;
}
if (fr_hex2bin(buffer + 8, nthashhash, 16) != 16) {
RDEBUG2("Invalid output from ntlm_auth: NT_KEY has non-hex values");
return -1;
}
}
return 0;
}
static const uint8_t SHSpad1[40] =
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static const uint8_t SHSpad2[40] =
{ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
static const uint8_t magic1[27] =
{ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
static const uint8_t magic2[84] =
{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
0x6b, 0x65, 0x79, 0x2e };
static const uint8_t magic3[84] =
{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
0x6b, 0x65, 0x79, 0x2e };
static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
uint8_t *masterkey)
{
uint8_t digest[20];
fr_SHA1_CTX Context;
fr_SHA1Init(&Context);
fr_SHA1Update(&Context,nt_hashhash,16);
fr_SHA1Update(&Context,nt_response,24);
fr_SHA1Update(&Context,magic1,27);
fr_SHA1Final(digest,&Context);
memcpy(masterkey,digest,16);
}
static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
int keylen,int issend)
{
uint8_t digest[20];
const uint8_t *s;
fr_SHA1_CTX Context;
memset(digest,0,20);
if(issend) {
s = magic3;
} else {
s = magic2;
}
fr_SHA1Init(&Context);
fr_SHA1Update(&Context,masterkey,16);
fr_SHA1Update(&Context,SHSpad1,40);
fr_SHA1Update(&Context,s,84);
fr_SHA1Update(&Context,SHSpad2,40);
fr_SHA1Final(digest,&Context);
memcpy(sesskey,digest,keylen);
}
static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
uint8_t *sendkey,uint8_t *recvkey)
{
uint8_t masterkey[16];
mppe_GetMasterKey(nt_hashhash,nt_response,masterkey);
mppe_GetAsymmetricStartKey(masterkey,sendkey,16,1);
mppe_GetAsymmetricStartKey(masterkey,recvkey,16,0);
}
static void mppe_chap2_gen_keys128(uint8_t *nt_hashhash,uint8_t *response,
uint8_t *sendkey,uint8_t *recvkey)
{
uint8_t enckey1[16];
uint8_t enckey2[16];
mppe_chap2_get_keys128(nt_hashhash,response,enckey1,enckey2);
memcpy (sendkey, enckey1, 16);
memcpy (recvkey, enckey2, 16);
}
static int mschap_authorize(void * instance, REQUEST *request)
{
#define inst ((rlm_mschap_t *)instance)
VALUE_PAIR *challenge = NULL, *response = NULL;
challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
if (!challenge) {
return RLM_MODULE_NOOP;
}
response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
if (!response)
response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);
if (!response) {
RDEBUG2("Found MS-CHAP-Challenge, but no MS-CHAP-Response.");
return RLM_MODULE_NOOP;
}
if (pairfind(request->config_items, PW_AUTH_TYPE)) {
RDEBUG2("WARNING: Auth-Type already set. Not setting to MS-CHAP");
return RLM_MODULE_NOOP;
}
RDEBUG2("Found MS-CHAP attributes. Setting 'Auth-Type = %s'", inst->xlat_name);
if (!radius_pairmake(request, &request->config_items,
"Auth-Type", inst->auth_type, T_OP_EQ)) {
return RLM_MODULE_FAIL;
}
return RLM_MODULE_OK;
#undef inst
}
static int mschap_authenticate(void * instance, REQUEST *request)
{
#define inst ((rlm_mschap_t *)instance)
VALUE_PAIR *challenge = NULL;
VALUE_PAIR *response = NULL;
VALUE_PAIR *password = NULL;
VALUE_PAIR *lm_password, *nt_password, *smb_ctrl;
VALUE_PAIR *username;
uint8_t nthashhash[16];
char msch2resp[42];
char *username_string;
int chap = 0;
int do_ntlm_auth;
do_ntlm_auth = (inst->ntlm_auth != NULL);
if (do_ntlm_auth) {
VALUE_PAIR *vp = pairfind(request->config_items,
PW_MS_CHAP_USE_NTLM_AUTH);
if (vp) do_ntlm_auth = vp->vp_integer;
}
smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
if (!smb_ctrl) {
password = pairfind(request->config_items,
PW_SMB_ACCOUNT_CTRL_TEXT);
if (password) {
smb_ctrl = radius_pairmake(request,
&request->config_items,
"SMB-Account-CTRL", "0",
T_OP_SET);
if (smb_ctrl) {
smb_ctrl->vp_integer = pdb_decode_acct_ctrl(password->vp_strvalue);
}
}
}
if (smb_ctrl) {
if ((smb_ctrl->vp_integer & ACB_PWNOTREQ) != 0) {
RDEBUG2("SMB-Account-Ctrl says no password is required.");
return RLM_MODULE_OK;
}
}
password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD);
lm_password = pairfind(request->config_items, PW_LM_PASSWORD);
if (lm_password) {
if ((lm_password->length == 16) ||
((lm_password->length == 32) &&
(fr_hex2bin(lm_password->vp_strvalue,
lm_password->vp_octets, 16) == 16))) {
RDEBUG2("Found LM-Password");
lm_password->length = 16;
} else {
radlog_request(L_ERR, 0, request, "Invalid LM-Password");
lm_password = NULL;
}
} else if (!password) {
if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create LM-Password.");
} else {
lm_password = radius_pairmake(request, &request->config_items,
"LM-Password", "", T_OP_EQ);
if (!lm_password) {
radlog_request(L_ERR, 0, request, "No memory");
} else {
smbdes_lmpwdhash(password->vp_strvalue,
lm_password->vp_octets);
lm_password->length = 16;
}
}
nt_password = pairfind(request->config_items, PW_NT_PASSWORD);
if (nt_password) {
if ((nt_password->length == 16) ||
((nt_password->length == 32) &&
(fr_hex2bin(nt_password->vp_strvalue,
nt_password->vp_octets, 16) == 16))) {
RDEBUG2("Found NT-Password");
nt_password->length = 16;
} else {
radlog_request(L_ERR, 0, request, "Invalid NT-Password");
nt_password = NULL;
}
} else if (!password) {
if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create NT-Password.");
} else {
nt_password = radius_pairmake(request, &request->config_items,
"NT-Password", "", T_OP_EQ);
if (!nt_password) {
radlog_request(L_ERR, 0, request, "No memory");
return RLM_MODULE_FAIL;
} else {
mschap_ntpwdhash(nt_password->vp_octets,
password->vp_strvalue);
nt_password->length = 16;
}
}
challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
if (!challenge) {
RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!");
return RLM_MODULE_REJECT;
}
response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
if (response) {
int offset;
if (challenge->length < 8) {
radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format.");
return RLM_MODULE_INVALID;
}
if (response->length < 50) {
radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format.");
return RLM_MODULE_INVALID;
}
if (response->vp_octets[1] & 0x01) {
RDEBUG2("Told to do MS-CHAPv1 with NT-Password");
password = nt_password;
offset = 26;
} else {
RDEBUG2("Told to do MS-CHAPv1 with LM-Password");
password = lm_password;
offset = 2;
}
if (do_mschap(inst, request, password, challenge->vp_octets,
response->vp_octets + offset, nthashhash,
do_ntlm_auth) < 0) {
RDEBUG2("MS-CHAP-Response is incorrect.");
goto do_error;
}
chap = 1;
} else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) {
uint8_t mschapv1_challenge[16];
VALUE_PAIR *name_attr, *response_name;
if (challenge->length < 16) {
radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format.");
return RLM_MODULE_INVALID;
}
if (response->length < 50) {
radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format.");
return RLM_MODULE_INVALID;
}
username = pairfind(request->packet->vps, PW_USER_NAME);
if (!username) {
radlog_request(L_AUTH, 0, request, "We require a User-Name for MS-CHAPv2");
return RLM_MODULE_INVALID;
}
response_name = pairfind(request->packet->vps, PW_MS_CHAP_USER_NAME);
if (response_name) {
name_attr = response_name;
} else {
name_attr = username;
}
if ((username_string = strchr(name_attr->vp_strvalue, '\\')) != NULL) {
if (inst->with_ntdomain_hack) {
username_string++;
} else {
RDEBUG2("NT Domain delimeter found, should we have enabled with_ntdomain_hack?");
username_string = name_attr->vp_strvalue;
}
} else {
username_string = name_attr->vp_strvalue;
}
if (response_name &&
((username->length != response_name->length) ||
(strncasecmp(username->vp_strvalue, response_name->vp_strvalue, username->length) != 0))) {
RDEBUG("ERROR: User-Name (%s) is not the same as MS-CHAP Name (%s) from EAP-MSCHAPv2", username->vp_strvalue, response_name->vp_strvalue);
return RLM_MODULE_REJECT;
}
RDEBUG2("Creating challenge hash with username: %s",
username_string);
mschap_challenge_hash(response->vp_octets + 2,
challenge->vp_octets,
username_string,
mschapv1_challenge);
RDEBUG2("Told to do MS-CHAPv2 for %s with NT-Password",
username_string);
#ifdef __APPLE__
if (inst->open_directory) {
return do_od_mschap(request, response, challenge, username_string);
}
#endif
if (do_mschap(inst, request, nt_password, mschapv1_challenge,
response->vp_octets + 26, nthashhash,
do_ntlm_auth) < 0) {
int i;
char buffer[128];
RDEBUG2("FAILED: MS-CHAP2-Response is incorrect");
do_error:
snprintf(buffer, sizeof(buffer), "E=691 R=%d",
inst->allow_retry);
if (inst->retry_msg) {
snprintf(buffer + 9, sizeof(buffer) - 9, " C=");
for (i = 0; i < 16; i++) {
snprintf(buffer + 12 + i*2,
sizeof(buffer) - 12 - i*2, "%02x",
fr_rand() & 0xff);
}
snprintf(buffer + 12 + 32, sizeof(buffer) - 45,
" V=3 M=%s", inst->retry_msg);
}
mschap_add_reply(request, &request->reply->vps,
*response->vp_octets, "MS-CHAP-Error",
buffer, strlen(buffer));
return RLM_MODULE_REJECT;
}
mschap_auth_response(username_string,
nthashhash,
response->vp_octets + 26,
response->vp_octets + 2,
challenge->vp_octets,
msch2resp);
mschap_add_reply(request, &request->reply->vps, *response->vp_octets,
"MS-CHAP2-Success", msch2resp, 42);
chap = 2;
} else {
RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!");
return RLM_MODULE_INVALID;
}
if (smb_ctrl) {
if (((smb_ctrl->vp_integer & ACB_DISABLED) != 0) ||
((smb_ctrl->vp_integer & (ACB_NORMAL|ACB_WSTRUST)) == 0)) {
RDEBUG2("SMB-Account-Ctrl says that the account is disabled, or is not a normal or workstatin trust account.");
mschap_add_reply(request, &request->reply->vps,
*response->vp_octets,
"MS-CHAP-Error", "E=691 R=1", 9);
return RLM_MODULE_NOTFOUND;
}
if ((smb_ctrl->vp_integer & ACB_AUTOLOCK) != 0) {
RDEBUG2("SMB-Account-Ctrl says that the account is locked out.");
mschap_add_reply(request, &request->reply->vps,
*response->vp_octets,
"MS-CHAP-Error", "E=647 R=0", 9);
return RLM_MODULE_USERLOCK;
}
}
if (inst->use_mppe) {
uint8_t mppe_sendkey[34];
uint8_t mppe_recvkey[34];
if (chap == 1){
RDEBUG2("adding MS-CHAPv1 MPPE keys");
memset(mppe_sendkey, 0, 32);
if (lm_password) {
memcpy(mppe_sendkey, lm_password->vp_octets, 8);
}
memcpy(mppe_sendkey + 8,
nthashhash, 16);
mppe_add_reply(request,
"MS-CHAP-MPPE-Keys",
mppe_sendkey, 32);
} else if (chap == 2) {
RDEBUG2("adding MS-CHAPv2 MPPE keys");
mppe_chap2_gen_keys128(nthashhash,
response->vp_octets + 26,
mppe_sendkey, mppe_recvkey);
mppe_add_reply(request,
"MS-MPPE-Recv-Key",
mppe_recvkey, 16);
mppe_add_reply(request,
"MS-MPPE-Send-Key",
mppe_sendkey, 16);
}
radius_pairmake(request, &request->reply->vps,
"MS-MPPE-Encryption-Policy",
(inst->require_encryption)? "0x00000002":"0x00000001",
T_OP_EQ);
radius_pairmake(request, &request->reply->vps,
"MS-MPPE-Encryption-Types",
(inst->require_strong)? "0x00000004":"0x00000006",
T_OP_EQ);
}
return RLM_MODULE_OK;
#undef inst
}
module_t rlm_mschap = {
RLM_MODULE_INIT,
"MS-CHAP",
RLM_TYPE_THREAD_SAFE | RLM_TYPE_HUP_SAFE,
mschap_instantiate,
mschap_detach,
{
mschap_authenticate,
mschap_authorize,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
},
};