*
* radius.c
*
-* RADIUS plugin for pppd. Performs PAP and CHAP authentication using
-* RADIUS.
+* RADIUS plugin for pppd. Performs PAP, CHAP, MS-CHAP, MS-CHAPv2
+* authentication using RADIUS.
*
* Copyright (C) 2002 Roaring Penguin Software Inc.
*
*
***********************************************************************/
static char const RCSID[] =
-"$Id: radius.c,v 1.4 2002/03/01 15:16:51 dfs Exp $";
+"$Id: radius.c,v 1.7 2002/03/12 00:28:56 dfs Exp $";
#include "pppd.h"
#include "chap.h"
+#ifdef CHAPMS
+#include "chap_ms.h"
+#endif
#include "radiusclient.h"
#include "fsm.h"
#include "ipcp.h"
static void radius_ip_up(void *opaque, int arg);
static void radius_ip_down(void *opaque, int arg);
static void make_username_realm(char *user);
-static int radius_setparams(VALUE_PAIR *vp, char *msg);
+static int radius_setparams(chap_state *cstate, VALUE_PAIR *vp, char *msg);
static void radius_choose_ip(u_int32_t *addrp);
static int radius_init(char *msg);
static int get_client_port(char *ifname);
}
if (result == OK_RC) {
- if (radius_setparams(received, radius_msg) < 0) {
+ if (radius_setparams(NULL, received, radius_msg) < 0) {
result = ERROR_RC;
}
}
* %RETURNS:
* CHAP_SUCCESS if we can authenticate, CHAP_FAILURE if we cannot.
* %DESCRIPTION:
-* Performs CHAP authentication using RADIUS
+* Performs CHAP, MS-CHAP and MS-CHAPv2 authentication using RADIUS
***********************************************************************/
static int
radius_chap_auth(char *user,
UINT4 av_type;
static char radius_msg[BUF_LEN];
int result;
- u_char cpassword[MD5_SIGNATURE_SIZE+1];
+ u_char cpassword[MAX_RESPONSE_LENGTH + 1];
radius_msg[0] = 0;
if (radius_init(radius_msg) < 0) {
return CHAP_FAILURE;
}
- /* we handle md5 digest at the moment */
- if (cstate->chal_type != CHAP_DIGEST_MD5) {
- error("RADIUS: Challenge type not MD5");
+ /* return error for types we can't handle */
+ if ((cstate->chal_type != CHAP_DIGEST_MD5)
+#ifdef CHAPMS
+ && (cstate->chal_type != CHAP_MICROSOFT)
+ && (cstate->chal_type != CHAP_MICROSOFT_V2)
+#endif
+ ) {
+ error("RADIUS: Challenge type %u unsupported", cstate->chal_type);
return CHAP_FAILURE;
}
rc_avpair_add (&send, PW_USER_NAME, rstate.user , 0, VENDOR_NONE);
/*
- * add the CHAP-Password and CHAP-Challenge fields
+ * add the challenge and response fields
*/
+ switch (cstate->chal_type) {
+ case CHAP_DIGEST_MD5:
+ /* CHAP-Challenge and CHAP-Password */
+ cpassword[0] = cstate->chal_id;
+ memcpy(&cpassword[1], remmd, MD5_SIGNATURE_SIZE);
+
+ rc_avpair_add(&send, PW_CHAP_CHALLENGE,
+ cstate->challenge, cstate->chal_len, VENDOR_NONE);
+ rc_avpair_add(&send, PW_CHAP_PASSWORD,
+ cpassword, MD5_SIGNATURE_SIZE + 1, VENDOR_NONE);
+ break;
+
+#ifdef CHAPMS
+ case CHAP_MICROSOFT:
+ {
+ /* MS-CHAP-Challenge and MS-CHAP-Response */
+ MS_ChapResponse *rmd = (MS_ChapResponse *) remmd;
+ u_char *p = cpassword;
+
+ *p++ = cstate->chal_id;
+ /* The idiots use a different field order in RADIUS than PPP */
+ memcpy(p, rmd->UseNT, sizeof(rmd->UseNT));
+ p += sizeof(rmd->UseNT);
+ memcpy(p, rmd->LANManResp, sizeof(rmd->LANManResp));
+ p += sizeof(rmd->LANManResp);
+ memcpy(p, rmd->NTResp, sizeof(rmd->NTResp));
+
+ rc_avpair_add(&send, PW_MS_CHAP_CHALLENGE,
+ cstate->challenge, cstate->chal_len, VENDOR_MICROSOFT);
+ rc_avpair_add(&send, PW_MS_CHAP_RESPONSE,
+ cpassword, MS_CHAP_RESPONSE_LEN + 1, VENDOR_MICROSOFT);
+ break;
+ }
+
+ case CHAP_MICROSOFT_V2:
+ {
+ /* MS-CHAP-Challenge and MS-CHAP2-Response */
+ MS_Chap2Response *rmd = (MS_Chap2Response *) remmd;
+ u_char *p = cpassword;
+
+ *p++ = cstate->chal_id;
+ /* The idiots use a different field order in RADIUS than PPP */
+ memcpy(p, rmd->Flags, sizeof(rmd->Flags));
+ p += sizeof(rmd->Flags);
+ memcpy(p, rmd->PeerChallenge, sizeof(rmd->PeerChallenge));
+ p += sizeof(rmd->PeerChallenge);
+ memcpy(p, rmd->Reserved, sizeof(rmd->Reserved));
+ p += sizeof(rmd->Reserved);
+ memcpy(p, rmd->NTResp, sizeof(rmd->NTResp));
+
+ rc_avpair_add(&send, PW_MS_CHAP_CHALLENGE,
+ cstate->challenge, cstate->chal_len, VENDOR_MICROSOFT);
+ rc_avpair_add(&send, PW_MS_CHAP2_RESPONSE,
+ cpassword, MS_CHAP2_RESPONSE_LEN + 1, VENDOR_MICROSOFT);
+ break;
+ }
+#endif
- cpassword[0] = cstate->chal_id;
-
- memcpy(&cpassword[1], remmd, MD5_SIGNATURE_SIZE);
-
- rc_avpair_add(&send, PW_CHAP_PASSWORD, cpassword, MD5_SIGNATURE_SIZE + 1, VENDOR_NONE);
-
- rc_avpair_add(&send, PW_CHAP_CHALLENGE, cstate->challenge, cstate->chal_len, VENDOR_NONE);
+ }
/*
* make authentication with RADIUS server
if (result == OK_RC) {
if (!rstate.done_chap_once) {
- if (radius_setparams(received, radius_msg) < 0) {
+ if (radius_setparams(cstate, received, radius_msg) < 0) {
error("%s", radius_msg);
result = ERROR_RC;
} else {
/**********************************************************************
* %FUNCTION: radius_setparams
* %ARGUMENTS:
+* cstate -- pppd's chap_state structure
* vp -- received value-pairs
* msg -- buffer in which to place error message. Holds up to BUF_LEN chars
* %RETURNS:
* >= 0 on success; -1 on failure
* %DESCRIPTION:
* Parses attributes sent by RADIUS server and sets them in pppd. Currently,
-* used only to set IP address.
+* used only to set IP address and MS-CHAPv2 Authenticator Response.
***********************************************************************/
static int
-radius_setparams(VALUE_PAIR *vp, char *msg)
+radius_setparams(chap_state *cstate, VALUE_PAIR *vp, char *msg)
{
u_int32_t remote;
+ int ms_chap2_success = 0;
/* Send RADIUS attributes to anyone else who might be interested */
if (radius_attributes_hook) {
*/
while (vp) {
- if (vp->vendorcode == VENDOR_NONE) {
- switch (vp->attribute) {
- case PW_SERVICE_TYPE:
- /* check for service type */
- /* if not FRAMED then exit */
- if (vp->lvalue != PW_FRAMED) {
- slprintf(msg, BUF_LEN, "RADIUS: wrong service type %ld for %s",
- vp->lvalue, rstate.user);
- return -1;
- }
- break;
- case PW_FRAMED_PROTOCOL:
- /* check for framed protocol type */
- /* if not PPP then also exit */
- if (vp->lvalue != PW_PPP) {
- slprintf(msg, BUF_LEN, "RADIUS: wrong framed protocol %ld for %s",
- vp->lvalue, rstate.user);
+ if ((vp->attribute == PW_SERVICE_TYPE) &&
+ (vp->vendorcode == VENDOR_NONE)) {
+ /* check for service type */
+ /* if not FRAMED then exit */
+ if (vp->lvalue != PW_FRAMED) {
+ slprintf(msg, BUF_LEN, "RADIUS: wrong service type %ld for %s",
+ vp->lvalue, rstate.user);
+ return -1;
+ }
+ } else if ((vp->attribute == PW_FRAMED_PROTOCOL) &&
+ (vp->vendorcode == VENDOR_NONE)) {
+ /* check for framed protocol type */
+ /* if not PPP then also exit */
+ if (vp->lvalue != PW_PPP) {
+ slprintf(msg, BUF_LEN, "RADIUS: wrong framed protocol %ld for %s",
+ vp->lvalue, rstate.user);
+ return -1;
+ }
+ } else if ((vp->attribute == PW_SESSION_TIMEOUT) &&
+ (vp->vendorcode == VENDOR_NONE)) {
+ /* Session timeout */
+ maxconnect = vp->lvalue;
+ } else if ((vp->attribute == PW_FRAMED_IP_ADDRESS) &&
+ (vp->vendorcode == VENDOR_NONE)) {
+ /* seting up remote IP addresses */
+ remote = vp->lvalue;
+ if (remote == 0xffffffff) {
+ /* 0xffffffff means user should be allowed to select one */
+ rstate.any_ip_addr_ok = 1;
+ } else if (remote != 0xfffffffe) {
+ /* 0xfffffffe means NAS should select an ip address */
+ remote = htonl(vp->lvalue);
+ if (bad_ip_adrs (remote)) {
+ slprintf(msg, BUF_LEN, "RADIUS: bad remote IP address %I for %s",
+ remote, rstate.user);
return -1;
}
- break;
-
- case PW_FRAMED_IP_ADDRESS:
- /* seting up remote IP addresses */
- remote = vp->lvalue;
- if (remote == 0xffffffff) {
- /* 0xffffffff means user should be allowed to select one */
- rstate.any_ip_addr_ok = 1;
- } else if (remote != 0xfffffffe) {
- /* 0xfffffffe means NAS should select an ip address */
- remote = htonl(vp->lvalue);
- if (bad_ip_adrs (remote)) {
- slprintf(msg, BUF_LEN, "RADIUS: bad remote IP address %I for %s",
- remote, rstate.user);
- return -1;
- }
- rstate.choose_ip = 1;
- rstate.ip_addr = remote;
- }
- break;
+ rstate.choose_ip = 1;
+ rstate.ip_addr = remote;
}
+#ifdef CHAPMS
+ } else if ((vp->attribute == PW_MS_CHAP2_SUCCESS) &&
+ (vp->vendorcode == VENDOR_MICROSOFT)) {
+ if ((vp->lvalue != 43) || strncmp(vp->strvalue + 1, "S=", 2)) {
+ slprintf(msg, BUF_LEN, "RADIUS: bad MS-CHAP2-Success packet");
+ return -1;
+ }
+ memcpy(cstate->saresponse, vp->strvalue + 3,
+ MS_AUTH_RESPONSE_LENGTH);
+ cstate->saresponse[MS_AUTH_RESPONSE_LENGTH] = '\0';
+ ms_chap2_success = 1;
+#endif /* CHAPMS */
}
vp = vp->next;
}
+
+ /* Require a valid MS-CHAP2-SUCCESS for MS-CHAPv2 auth */
+ if (cstate && (cstate->chal_type == CHAP_MICROSOFT_V2) && !ms_chap2_success)
+ return -1;
+
return 0;
}