From: David F. Skoll Date: Fri, 1 Mar 2002 14:39:19 +0000 (+0000) Subject: Large patch from Frank Cusack to add proper X-Git-Tag: ppp-2.4.7~441 X-Git-Url: https://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=c062322f9e8757b85a3c2281a3190d8af14bcd9b Large patch from Frank Cusack to add proper support for MS-CHAP (client and server are now supported.) Allow another plugin to select a different RADIUS server. Modified radiusclient library to include two new APIs: rc_acct_using_server and rc_auth_using_server in which caller specifies which RADIUS servers to use, instead of using the default ones in the config file. The /etc/radiusclient/servers file must still contain secrets for those servers. --- diff --git a/README.MSCHAP80 b/README.MSCHAP80 index d3ed291..fe7a019 100644 --- a/README.MSCHAP80 +++ b/README.MSCHAP80 @@ -1,10 +1,11 @@ -PPP Client Support for Microsoft's CHAP-80 -========================================== +PPP Support for Microsoft's CHAP-80 +=================================== Eric Rosenquist rosenqui@strataware.com (updated by Paul Mackerras) (updated by Al Longyear) (updated by Farrell Woods) +(updated by Frank Cusack) INTRODUCTION @@ -16,7 +17,7 @@ by a bogus client to gain access to the server just as easily as if the password were stored in cleartext.) The details of the Microsoft extensions can be found in the document: - + In short, MS-CHAP is identified as since the hex value of 80 is used to designate Microsoft's scheme. Standard PPP CHAP uses @@ -35,12 +36,7 @@ MS-CHAP by NAKing it: Windows NT Server systems are often configured to "Accept only Microsoft Authentication" (this is intended to enhance security). Up until now, that meant that you couldn't use this version of PPPD to -connect to such a system. I've managed to get a client-only -implementation of MS-CHAP working; it will authenticate itself to -another system using MS-CHAP, but if you're using PPPD as a dial-in -server, you won't be able to use MS-CHAP to authenticate the clients. -This would not be a lot of extra work given that the framework is in -place, but I didn't need it myself so I didn't implement it. +connect to such a system. BUILDING THE PPPD @@ -275,10 +271,5 @@ to be used in chap-secrets in place of the password. The code to do this could quite easily be lifted from chap_ms.c (you have to convert the password to Unicode before hashing it). The chap_ms.c file would also have to be changed to recognize a password hash (16 binary bytes == 32 ASCII hex -characters) and skip the hashing stage. - -A server implementation would allow MS-CHAP to be used with Windows NT and -Windows 95 clients for enhanced security. Some new command-line options -would be required, as would code to generate the Challenge packet and -verify the response. Most of the helper functions are in place, so this -shouldn't be too hard for someone to add. +characters) and skip the hashing stage. This would have no real security +value as the hash is plaintext-equivalent. diff --git a/pppd/auth.c b/pppd/auth.c index a5fc1bd..4f9c297 100644 --- a/pppd/auth.c +++ b/pppd/auth.c @@ -32,7 +32,7 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#define RCSID "$Id: auth.c,v 1.73 2002/01/22 16:02:58 dfs Exp $" +#define RCSID "$Id: auth.c,v 1.74 2002/03/01 14:39:18 dfs Exp $" #include #include @@ -169,6 +169,11 @@ bool uselogin = 0; /* Use /etc/passwd for checking PAP */ bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */ bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */ bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */ +#ifdef CHAPMS +bool refuse_mschap = 0; /* Don't wanna auth. ourselves with MS-CHAP */ +#else +bool refuse_mschap = 1; /* Don't wanna auth. ourselves with MS-CHAP */ +#endif bool usehostname = 0; /* Use hostname for our_name */ bool auth_required = 0; /* Always require authentication from peer */ bool allow_any_ip = 0; /* Allow peer to use any IP address */ @@ -218,30 +223,53 @@ option_t auth_options[] = { { "auth", o_bool, &auth_required, "Require authentication from peer", OPT_PRIO | 1 }, { "noauth", o_bool, &auth_required, - "Don't require peer to authenticate", OPT_PRIOSUB | OPT_PRIV, + "Don't require peer to authenticate", OPT_PRIOSUB | OPT_PRIV | OPT_A2COPY, &allow_any_ip }, { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap, "Require PAP authentication from peer", - OPT_PRIOSUB | 1, &auth_required }, + OPT_PRIOSUB | OPT_A2COPY | 1, &auth_required }, { "+pap", o_bool, &lcp_wantoptions[0].neg_upap, "Require PAP authentication from peer", - OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required }, + OPT_ALIAS | OPT_PRIOSUB | OPT_A2COPY | 1, &auth_required }, { "require-chap", o_bool, &lcp_wantoptions[0].neg_chap, "Require CHAP authentication from peer", - OPT_PRIOSUB | 1, &auth_required }, + OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MD5, + &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype }, { "+chap", o_bool, &lcp_wantoptions[0].neg_chap, "Require CHAP authentication from peer", - OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required }, + OPT_ALIAS | OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MD5, + &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype }, +#ifdef CHAPMS + { "require-mschap", o_bool, &lcp_wantoptions[0].neg_chap, + "Require MS-CHAP authentication from peer", + OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MICROSOFT, + &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype }, + { "+mschap", o_bool, &lcp_wantoptions[0].neg_chap, + "Require MS-CHAP authentication from peer", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2COPY | OPT_A3OR | MDTYPE_MICROSOFT, + &auth_required, 0, 0, NULL, 0, 0, &lcp_wantoptions[0].chap_mdtype }, +#endif { "refuse-pap", o_bool, &refuse_pap, "Don't agree to auth to peer with PAP", 1 }, { "-pap", o_bool, &refuse_pap, "Don't allow PAP authentication with peer", OPT_ALIAS | 1 }, - { "refuse-chap", o_bool, &refuse_chap, - "Don't agree to auth to peer with CHAP", 1 }, + "Don't agree to auth to peer with CHAP", OPT_A2CLRB | MDTYPE_MD5, + &lcp_allowoptions[0].chap_mdtype }, { "-chap", o_bool, &refuse_chap, - "Don't allow CHAP authentication with peer", OPT_ALIAS | 1 }, + "Don't allow CHAP authentication with peer", + OPT_ALIAS | OPT_A2CLRB | MDTYPE_MD5, + &lcp_allowoptions[0].chap_mdtype }, +#ifdef CHAPMS + { "refuse-mschap", o_bool, &refuse_mschap, + "Don't agree to auth to peer with MS-CHAP", OPT_A2CLRB | MDTYPE_MICROSOFT, + &lcp_allowoptions[0].chap_mdtype }, + { "-mschap", o_bool, &refuse_mschap, + "Don't allow MS-CHAP authentication with peer", + OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT, + &lcp_allowoptions[0].chap_mdtype }, +#endif { "name", o_string, our_name, "Set local name for authentication", @@ -466,12 +494,12 @@ link_established(unit) && protp->lowerup != NULL) (*protp->lowerup)(unit); - if (auth_required && !(go->neg_chap || go->neg_upap)) { + if (auth_required && !(go->neg_upap || go->neg_chap)) { /* * We wanted the peer to authenticate itself, and it refused: * if we have some address(es) it can use without auth, fine, * otherwise treat it as though it authenticated with PAP using - * a username * of "" and a password of "". If that's not OK, + * a username of "" and a password of "". If that's not OK, * boot it out. */ if (noauth_addrs != NULL) { @@ -488,14 +516,14 @@ link_established(unit) used_login = 0; auth = 0; if (go->neg_chap) { - ChapAuthPeer(unit, our_name, go->chap_mdtype); + ChapAuthPeer(unit, our_name, CHAP_DIGEST(go->chap_mdtype)); auth |= CHAP_PEER; } else if (go->neg_upap) { upap_authpeer(unit); auth |= PAP_PEER; } if (ho->neg_chap) { - ChapAuthWithPeer(unit, user, ho->chap_mdtype); + ChapAuthWithPeer(unit, user, CHAP_DIGEST(ho->chap_mdtype)); auth |= CHAP_WITHPEER; } else if (ho->neg_upap) { if (passwd[0] == 0) { @@ -834,11 +862,11 @@ auth_check_options() if (auth_required) { allow_any_ip = 0; if (!wo->neg_chap && !wo->neg_upap) { - wo->neg_chap = 1; + wo->neg_chap = 1; wo->chap_mdtype = MDTYPE_ALL; wo->neg_upap = 1; } } else { - wo->neg_chap = 0; + wo->neg_chap = 0; wo->chap_mdtype = MDTYPE_NONE; wo->neg_upap = 0; } @@ -848,7 +876,7 @@ auth_check_options() */ lacks_ip = 0; can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip)); - if (!can_auth && wo->neg_chap) { + if (!can_auth && (wo->neg_chap)) { can_auth = have_chap_secret((explicit_remote? remote_name: NULL), our_name, 1, &lacks_ip); } @@ -889,7 +917,7 @@ auth_reset(unit) lcp_options *ao = &lcp_allowoptions[0]; ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL)); - ao->neg_chap = !refuse_chap + ao->neg_chap = (!refuse_chap || !refuse_mschap) && (passwd[0] != 0 || have_chap_secret(user, (explicit_remote? remote_name: NULL), 0, NULL)); diff --git a/pppd/chap.c b/pppd/chap.c index 9bd85b5..25427e2 100644 --- a/pppd/chap.c +++ b/pppd/chap.c @@ -1,5 +1,5 @@ /* - * chap.c - Challenge Handshake Authentication Protocol. + * chap_ms.c - Challenge Handshake Authentication Protocol. * * Copyright (c) 1993 The Australian National University. * All rights reserved. @@ -33,7 +33,7 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#define RCSID "$Id: chap.c,v 1.26 2002/01/22 16:02:58 dfs Exp $" +#define RCSID "$Id: chap.c,v 1.27 2002/03/01 14:39:18 dfs Exp $" /* * TODO: @@ -579,20 +579,48 @@ ChapReceiveResponse(cstate, inp, id, len) /* generate MD based on negotiated type */ switch (cstate->chal_type) { - case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + case CHAP_DIGEST_MD5: if (remmd_len != MD5_SIGNATURE_SIZE) - break; /* it's not even the right length */ + break; /* not even the right length */ MD5Init(&mdContext); MD5Update(&mdContext, &cstate->chal_id, 1); MD5Update(&mdContext, secret, secret_len); MD5Update(&mdContext, cstate->challenge, cstate->chal_len); MD5Final(hash, &mdContext); - /* compare local and remote MDs and send the appropriate status */ + /* compare MDs and send the appropriate status */ if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) code = CHAP_SUCCESS; /* they are the same! */ break; +#ifdef CHAPMS + case CHAP_MICROSOFT: + { + int response_offset, response_size; + + if (remmd_len != MS_CHAP_RESPONSE_LEN) + break; /* not even the right length */ + ChapMS(cstate, cstate->challenge, cstate->chal_len, + secret, secret_len); + + /* Determine which part of response to verify against */ + if ((u_char *) (remmd + offsetof(MS_ChapResponse, UseNT))) { + response_offset = offsetof(MS_ChapResponse, NTResp); + response_size = sizeof(((MS_ChapResponse *) remmd)->NTResp); + } else { + response_offset = offsetof(MS_ChapResponse, LANManResp); + response_size = + sizeof(((MS_ChapResponse *) remmd)->LANManResp); + } + + /* compare MDs and send the appropriate status */ + if (memcmp(cstate->response + response_offset, + remmd + response_offset, response_size) == 0) + code = CHAP_SUCCESS; /* they are the same! */ + break; + } +#endif /* CHAPMS */ + default: CHAPDEBUG(("unknown digest type %d", cstate->chal_type)); } @@ -759,15 +787,33 @@ static void ChapGenChallenge(cstate) chap_state *cstate; { - int chal_len; + int chal_len = 0; /* Avoid compiler warning */ u_char *ptr = cstate->challenge; int i; - /* pick a random challenge length between MIN_CHALLENGE_LENGTH and - MAX_CHALLENGE_LENGTH */ - chal_len = (unsigned) ((drand48() * - (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) + - MIN_CHALLENGE_LENGTH); + switch (cstate->chal_type) { + case CHAP_DIGEST_MD5: + /* + * pick a random challenge length between MIN_CHALLENGE_LENGTH and + * MAX_CHALLENGE_LENGTH + */ + chal_len = (unsigned) ((drand48() * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) + + MIN_CHALLENGE_LENGTH); + break; + +#ifdef CHAPMS + case CHAP_MICROSOFT: + /* MS-CHAP is fixed to an 8 octet challenge. */ + chal_len = 8; + break; +#endif + default: + fatal("ChapGenChallenge: Unsupported challenge type %d", + (int) cstate->chal_type); + break; + } + cstate->chal_len = chal_len; cstate->chal_id = ++cstate->id; cstate->chal_transmits = 0; diff --git a/pppd/chap.h b/pppd/chap.h index 37d1665..d15b948 100644 --- a/pppd/chap.h +++ b/pppd/chap.h @@ -30,7 +30,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: chap.h,v 1.9 2002/01/22 16:02:58 dfs Exp $ + * $Id: chap.h,v 1.10 2002/03/01 14:39:18 dfs Exp $ */ #ifndef __CHAP_INCLUDE__ @@ -45,7 +45,42 @@ #define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ #define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ #define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */ -#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ + +/* + * Digest type and selection. + */ + +/* bitmask of supported algorithms */ +#define MDTYPE_MD5 0x1 +#define MDTYPE_MICROSOFT 0x2 + +#ifdef CHAPMS +#define MDTYPE_ALL (MDTYPE_MD5 | MDTYPE_MICROSOFT) +#else +#define MDTYPE_ALL (MDTYPE_MD5) +#endif +#define MDTYPE_NONE 0 + +/* Return the digest alg. ID for the most preferred digest type. */ +#define CHAP_DIGEST(mdtype) \ + ((mdtype) & MDTYPE_MD5)? CHAP_DIGEST_MD5: \ + ((mdtype) & MDTYPE_MICROSOFT)? CHAP_MICROSOFT: \ + 0 + +/* Return the bit flag (lsb set) for our most preferred digest type. */ +#define CHAP_MDTYPE(mdtype) ((mdtype) ^ ((mdtype) - 1)) & (mdtype) + +/* Return the bit flag for a given digest algorithm ID. */ +#define CHAP_MDTYPE_D(digest) \ + ((digest) == CHAP_DIGEST_MD5)? MDTYPE_MD5: \ + ((digest) == CHAP_MICROSOFT)? MDTYPE_MICROSOFT: \ + 0 + +/* Can we do the requested digest? */ +#define CHAP_CANDIGEST(mdtype, digest) \ + ((digest) == CHAP_DIGEST_MD5)? (mdtype) & MDTYPE_MD5: \ + ((digest) == CHAP_MICROSOFT)? (mdtype) & MDTYPE_MICROSOFT: \ + 0 #define CHAP_CHALLENGE 1 #define CHAP_RESPONSE 2 diff --git a/pppd/chap_ms.c b/pppd/chap_ms.c index eb0c2c1..9a7002c 100644 --- a/pppd/chap_ms.c +++ b/pppd/chap_ms.c @@ -31,7 +31,7 @@ * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 */ -#define RCSID "$Id: chap_ms.c,v 1.15 1999/08/13 06:46:12 paulus Exp $" +#define RCSID "$Id: chap_ms.c,v 1.16 2002/03/01 14:39:18 dfs Exp $" #ifdef CHAPMS @@ -57,14 +57,6 @@ static const char rcsid[] = RCSID; -typedef struct { - u_char LANManResp[24]; - u_char NTResp[24]; - u_char UseNT; /* If 1, ignore the LANMan response field */ -} MS_ChapResponse; -/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), - in case this struct gets padded. */ - static void ChallengeResponse __P((u_char *, u_char *, u_char *)); static void DesEncrypt __P((u_char *, u_char *, u_char *)); diff --git a/pppd/chap_ms.h b/pppd/chap_ms.h index 1f0ea2b..e673f8a 100644 --- a/pppd/chap_ms.h +++ b/pppd/chap_ms.h @@ -1,5 +1,5 @@ /* - * chap.h - Challenge Handshake Authentication Protocol definitions. + * chap_ms.h - Challenge Handshake Authentication Protocol definitions. * * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. * http://www.strataware.com/ @@ -19,13 +19,25 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: chap_ms.h,v 1.2 1997/11/27 06:08:10 paulus Exp $ + * $Id: chap_ms.h,v 1.3 2002/03/01 14:39:18 dfs Exp $ */ #ifndef __CHAPMS_INCLUDE__ #define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */ -#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */ +#define MAX_NT_PASSWORD 256 /* Max (Unicode) chars in an NT pass */ + +#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ + +/* + * Use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), + * in case this struct gets padded. + */ +typedef struct { + u_char LANManResp[24]; + u_char NTResp[24]; + u_char UseNT; /* If 1, ignore the LANMan response field */ +} MS_ChapResponse; void ChapMS __P((chap_state *, char *, int, char *, int)); diff --git a/pppd/lcp.c b/pppd/lcp.c index d708c27..8cf6029 100644 --- a/pppd/lcp.c +++ b/pppd/lcp.c @@ -17,7 +17,7 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#define RCSID "$Id: lcp.c,v 1.57 2001/03/08 05:11:14 paulus Exp $" +#define RCSID "$Id: lcp.c,v 1.58 2002/03/01 14:39:18 dfs Exp $" /* * TODO: @@ -327,7 +327,6 @@ lcp_init(unit) wo->neg_mru = 1; wo->mru = DEFMRU; wo->neg_asyncmap = 1; - wo->chap_mdtype = CHAP_DIGEST_MD5; wo->neg_magicnumber = 1; wo->neg_pcompression = 1; wo->neg_accompression = 1; @@ -337,7 +336,7 @@ lcp_init(unit) ao->mru = MAXMRU; ao->neg_asyncmap = 1; ao->neg_chap = 1; - ao->chap_mdtype = CHAP_DIGEST_MD5; + ao->chap_mdtype = MDTYPE_ALL; ao->neg_upap = 1; ao->neg_magicnumber = 1; ao->neg_pcompression = 1; @@ -679,10 +678,10 @@ lcp_addci(f, ucp, lenp) } #define ADDCICHAP(opt, neg, val, digest) \ if (neg) { \ - PUTCHAR(opt, ucp); \ + PUTCHAR((opt), ucp); \ PUTCHAR(CILEN_CHAP, ucp); \ - PUTSHORT(val, ucp); \ - PUTCHAR(digest, ucp); \ + PUTSHORT((val), ucp); \ + PUTCHAR((digest), ucp); \ } #define ADDCILONG(opt, neg, val) \ if (neg) { \ @@ -716,7 +715,7 @@ lcp_addci(f, ucp, lenp) ADDCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru); ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, go->asyncmap); - ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP,CHAP_DIGEST(go->chap_mdtype)); ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); @@ -802,13 +801,13 @@ lcp_ackci(f, p, len) GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_CHAP || \ - citype != opt) \ + citype != (opt)) \ goto bad; \ GETSHORT(cishort, p); \ - if (cishort != val) \ + if (cishort != (val)) \ goto bad; \ GETCHAR(cichar, p); \ - if (cichar != digest) \ + if (cichar != (digest)) \ goto bad; \ } #define ACKCILONG(opt, neg, val) \ @@ -863,7 +862,7 @@ lcp_ackci(f, p, len) ACKCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru); ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, go->asyncmap); - ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP,CHAP_DIGEST(go->chap_mdtype)); ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); @@ -1035,7 +1034,7 @@ lcp_nakci(f, p, len) no.neg_chap = go->neg_chap; no.neg_upap = go->neg_upap; INCPTR(2, p); - GETSHORT(cishort, p); + GETSHORT(cishort, p); if (cishort == PPP_PAP && cilen == CILEN_SHORT) { /* * If we were asking for CHAP, they obviously don't want to do it. @@ -1050,18 +1049,25 @@ lcp_nakci(f, p, len) GETCHAR(cichar, p); if (go->neg_chap) { /* - * We were asking for CHAP/MD5; they must want a different - * algorithm. If they can't do MD5, we can ask for M$-CHAP - * if we support it, otherwise we'll have to stop - * asking for CHAP. + * We were asking for our preferred algorithm, they must + * want something different. */ - if (cichar != go->chap_mdtype) { -#ifdef CHAPMS - if (cichar == CHAP_MICROSOFT) - go->chap_mdtype = CHAP_MICROSOFT; - else -#endif /* CHAPMS */ - try.neg_chap = 0; + if (cichar != CHAP_DIGEST(go->chap_mdtype)) { + if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) { + /* Use their suggestion if we support it ... */ + go->chap_mdtype = CHAP_MDTYPE_D(cichar); + } else { + /* ... otherwise, try our next-preferred algorithm. */ + go->chap_mdtype &= ~(CHAP_MDTYPE(go->chap_mdtype)); + if (go->chap_mdtype == MDTYPE_NONE) /* out of algos */ + try.neg_chap = 0; + } + } else { + /* + * Whoops, they Nak'd our algorithm of choice + * but then suggested it back to us. + */ + goto bad; } } else { /* @@ -1304,7 +1310,7 @@ lcp_rejci(f, p, len) GETSHORT(cishort, p); \ GETCHAR(cichar, p); \ /* Check rejected value. */ \ - if (cishort != val || cichar != digest) \ + if ((cishort != (val)) || (cichar != (digest))) \ goto bad; \ try.neg = 0; \ try.neg_upap = 0; \ @@ -1370,7 +1376,7 @@ lcp_rejci(f, p, len) REJCISHORT(CI_MRU, neg_mru, go->mru); REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); - REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype); + REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, CHAP_DIGEST(go->chap_mdtype)); if (!go->neg_chap) { REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); } @@ -1527,7 +1533,7 @@ lcp_reqci(f, inp, lenp, reject_if_disagree) * for UPAP, then we will reject the second request. * Whether we end up doing CHAP or UPAP depends then on * the ordering of the CIs in the peer's Configure-Request. - */ + */ if (cishort == PPP_PAP) { if (ho->neg_chap || /* we've already accepted CHAP */ @@ -1541,9 +1547,7 @@ lcp_reqci(f, inp, lenp, reject_if_disagree) PUTCHAR(CI_AUTHTYPE, nakp); PUTCHAR(CILEN_CHAP, nakp); PUTSHORT(PPP_CHAP, nakp); - PUTCHAR(ao->chap_mdtype, nakp); - /* XXX if we can do CHAP_MICROSOFT as well, we should - probably put in another option saying so */ + PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp); break; } ho->neg_upap = 1; @@ -1563,20 +1567,20 @@ lcp_reqci(f, inp, lenp, reject_if_disagree) PUTSHORT(PPP_PAP, nakp); break; } - GETCHAR(cichar, p); /* get digest type*/ - if (cichar != CHAP_DIGEST_MD5 -#ifdef CHAPMS - && cichar != CHAP_MICROSOFT -#endif - ) { + GETCHAR(cichar, p); /* get digest type */ + if (!(CHAP_CANDIGEST(ao->chap_mdtype, cichar))) { + /* + * We can't/won't do the requested type, + * suggest something else. + */ orc = CONFNAK; PUTCHAR(CI_AUTHTYPE, nakp); PUTCHAR(CILEN_CHAP, nakp); PUTSHORT(PPP_CHAP, nakp); - PUTCHAR(ao->chap_mdtype, nakp); + PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp); break; } - ho->chap_mdtype = cichar; /* save md type */ + ho->chap_mdtype = CHAP_MDTYPE_D(cichar); /* save md type */ ho->neg_chap = 1; break; } @@ -1591,7 +1595,7 @@ lcp_reqci(f, inp, lenp, reject_if_disagree) if (ao->neg_chap) { PUTCHAR(CILEN_CHAP, nakp); PUTSHORT(PPP_CHAP, nakp); - PUTCHAR(ao->chap_mdtype, nakp); + PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp); } else { PUTCHAR(CILEN_SHORT, nakp); PUTSHORT(PPP_PAP, nakp); diff --git a/pppd/lcp.h b/pppd/lcp.h index 5122b7b..95260bb 100644 --- a/pppd/lcp.h +++ b/pppd/lcp.h @@ -16,7 +16,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: lcp.h,v 1.16 2001/03/08 05:11:14 paulus Exp $ + * $Id: lcp.h,v 1.17 2002/03/01 14:39:18 dfs Exp $ */ /* @@ -64,7 +64,7 @@ typedef struct lcp_options { bool neg_endpoint; /* negotiate endpoint discriminator */ int mru; /* Value of MRU */ int mrru; /* Value of MRRU, and multilink enable */ - u_char chap_mdtype; /* which MD type (hashing algorithm) */ + u_char chap_mdtype; /* which MD types (hashing algorithm) */ u_int32_t asyncmap; /* Value of async map */ u_int32_t magicnumber; int numloops; /* Number of loops during magic number neg. */ diff --git a/pppd/options.c b/pppd/options.c index 3c1a295..3779b28 100644 --- a/pppd/options.c +++ b/pppd/options.c @@ -17,7 +17,7 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#define RCSID "$Id: options.c,v 1.81 2002/01/11 18:11:51 etbe Exp $" +#define RCSID "$Id: options.c,v 1.82 2002/03/01 14:39:18 dfs Exp $" #include #include @@ -623,6 +623,12 @@ process_option(opt, cmd, argv) *(bool *)(opt->addr) = v; if (opt->addr2 && (opt->flags & OPT_A2COPY)) *(bool *)(opt->addr2) = v; + else if (opt->addr2 && (opt->flags & OPT_A2CLR)) + *(bool *)(opt->addr2) = 0; + else if (opt->addr2 && (opt->flags & OPT_A2CLRB)) + *(u_char *)(opt->addr2) &= ~v; + if (opt->addr3 && (opt->flags & OPT_A3OR)) + *(u_char *)(opt->addr3) |= v; break; case o_int: diff --git a/pppd/plugins/radius/radius.c b/pppd/plugins/radius/radius.c index 3dc69ae..c192bbb 100644 --- a/pppd/plugins/radius/radius.c +++ b/pppd/plugins/radius/radius.c @@ -21,7 +21,7 @@ * ***********************************************************************/ static char const RCSID[] = -"$Id: radius.c,v 1.2 2002/02/08 17:28:31 dfs Exp $"; +"$Id: radius.c,v 1.3 2002/03/01 14:39:18 dfs Exp $"; #include "pppd.h" #include "chap.h" @@ -61,9 +61,6 @@ static int radius_init(char *msg); static int get_client_port(char *ifname); static int radius_allowed_address(u_int32_t addr); -void (*radius_attributes_hook)(VALUE_PAIR *) = NULL; -void (*radius_pre_auth_hook)(char const *user) = NULL; - #ifndef MAXSESSIONID #define MAXSESSIONID 32 #endif @@ -80,8 +77,18 @@ struct radius_state { char config_file[MAXPATHLEN]; char session_id[MAXSESSIONID + 1]; time_t start_time; + SERVER *authserver; /* Authentication server to use */ + SERVER *acctserver; /* Accounting server to use */ }; +void (*radius_attributes_hook)(VALUE_PAIR *) = NULL; + +/* The pre_auth_hook MAY set authserver and acctserver if it wants. + In that case, they override the values in the radiusclient.conf file */ +void (*radius_pre_auth_hook)(char const *user, + SERVER **authserver, + SERVER **acctserver) = NULL; + static struct radius_state rstate; char pppd_version[] = VERSION; @@ -189,7 +196,9 @@ radius_pap_auth(char *user, make_username_realm(user); if (radius_pre_auth_hook) { - radius_pre_auth_hook(rstate.user); + radius_pre_auth_hook(rstate.user, + &rstate.authserver, + &rstate.acctserver); } send = NULL; @@ -212,7 +221,13 @@ radius_pap_auth(char *user, VENDOR_NONE); } - result = rc_auth(rstate.client_port, send, &received, radius_msg); + if (rstate.authserver) { + result = rc_auth_using_server(rstate.authserver, + rstate.client_port, send, + &received, radius_msg); + } else { + result = rc_auth(rstate.client_port, send, &received, radius_msg); + } if (result == OK_RC) { if (radius_setparams(received, radius_msg) < 0) { @@ -268,7 +283,9 @@ radius_chap_auth(char *user, make_username_realm(user); rstate.client_port = get_client_port (ifname); if (radius_pre_auth_hook) { - radius_pre_auth_hook(rstate.user); + radius_pre_auth_hook(rstate.user, + &rstate.authserver, + &rstate.acctserver); } } @@ -298,7 +315,13 @@ radius_chap_auth(char *user, * make authentication with RADIUS server */ - result = rc_auth (rstate.client_port, send, &received, radius_msg); + if (rstate.authserver) { + result = rc_auth_using_server(rstate.authserver, + rstate.client_port, send, + &received, radius_msg); + } else { + result = rc_auth(rstate.client_port, send, &received, radius_msg); + } if (result == OK_RC) { if (!rstate.done_chap_once) { @@ -474,7 +497,12 @@ radius_acct_start(void) av_type = htonl(hisaddr); rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE); - result = rc_acct(rstate.client_port, send); + if (rstate.acctserver) { + result = rc_acct_using_server(rstate.acctserver, + rstate.client_port, send); + } else { + result = rc_acct(rstate.client_port, send); + } rc_avpair_free(send); @@ -561,7 +589,13 @@ radius_acct_stop(void) av_type = htonl(hisaddr); rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE); - result = rc_acct(rstate.client_port, send); + if (rstate.acctserver) { + result = rc_acct_using_server(rstate.acctserver, + rstate.client_port, send); + } else { + result = rc_acct(rstate.client_port, send); + } + if (result != OK_RC) { /* RADIUS server could be down so make this a warning */ syslog(LOG_WARNING, diff --git a/pppd/plugins/radius/radiusclient/include/radiusclient.h b/pppd/plugins/radius/radiusclient/include/radiusclient.h index cff6822..5604987 100644 --- a/pppd/plugins/radius/radiusclient/include/radiusclient.h +++ b/pppd/plugins/radius/radiusclient/include/radiusclient.h @@ -1,5 +1,5 @@ /* - * $Id: radiusclient.h,v 1.2 2002/02/27 15:51:19 dfs Exp $ + * $Id: radiusclient.h,v 1.3 2002/03/01 14:39:19 dfs Exp $ * * Copyright (C) 1995,1996,1997,1998 Lars Fenneberg * @@ -379,8 +379,10 @@ VALUE_PAIR *rc_avpair_readin __P((FILE *)); void rc_buildreq __P((SEND_DATA *, int, char *, unsigned short, int, int)); unsigned char rc_get_seqnbr __P((void)); int rc_auth __P((UINT4, VALUE_PAIR *, VALUE_PAIR **, char *)); +int rc_auth_using_server __P((SERVER *, UINT4, VALUE_PAIR *, VALUE_PAIR **, char *)); int rc_auth_proxy __P((VALUE_PAIR *, VALUE_PAIR **, char *)); int rc_acct __P((UINT4, VALUE_PAIR *)); +int rc_acct_using_server __P((SERVER *, UINT4, VALUE_PAIR *)); int rc_acct_proxy __P((VALUE_PAIR *)); int rc_check __P((char *, unsigned short, char *)); diff --git a/pppd/plugins/radius/radiusclient/lib/buildreq.c b/pppd/plugins/radius/radiusclient/lib/buildreq.c index 507a25c..b7ca07e 100644 --- a/pppd/plugins/radius/radiusclient/lib/buildreq.c +++ b/pppd/plugins/radius/radiusclient/lib/buildreq.c @@ -1,5 +1,5 @@ /* - * $Id: buildreq.c,v 1.1 2002/01/22 16:03:02 dfs Exp $ + * $Id: buildreq.c,v 1.2 2002/03/01 14:39:19 dfs Exp $ * * Copyright (C) 1995,1997 Lars Fenneberg * @@ -121,12 +121,37 @@ unsigned char rc_get_seqnbr(void) int rc_auth(UINT4 client_port, VALUE_PAIR *send, VALUE_PAIR **received, char *msg) +{ + SERVER *authserver = rc_conf_srv("authserver"); + + if (!authserver) { + return (ERROR_RC); + } + return rc_auth_using_server(authserver, client_port, send, received, msg); +} + +/* + * Function: rc_auth_using_server + * + * Purpose: Builds an authentication request for port id client_port + * with the value_pairs send and submits it to a server. You + * explicitly supply a server list. + * + * Returns: received value_pairs in received, messages from the server in msg + * and 0 on success, negative on failure as return value + * + */ + +int rc_auth_using_server(SERVER *authserver, + UINT4 client_port, + VALUE_PAIR *send, + VALUE_PAIR **received, + char *msg) { SEND_DATA data; UINT4 client_id; int result; int i; - SERVER *authserver = rc_conf_srv("authserver"); int timeout = rc_conf_int("radius_timeout"); int retries = rc_conf_int("radius_retries"); @@ -215,16 +240,18 @@ int rc_auth_proxy(VALUE_PAIR *send, VALUE_PAIR **received, char *msg) /* - * Function: rc_acct + * Function: rc_acct_using_server * * Purpose: Builds an accounting request for port id client_port - * with the value_pairs send + * with the value_pairs send. You explicitly supply server list. * * Remarks: NAS-IP-Address, NAS-Port and Acct-Delay-Time get filled * in by this function, the rest has to be supplied. */ -int rc_acct(UINT4 client_port, VALUE_PAIR *send) +int rc_acct_using_server(SERVER *acctserver, + UINT4 client_port, + VALUE_PAIR *send) { SEND_DATA data; VALUE_PAIR *adt_vp; @@ -233,7 +260,6 @@ int rc_acct(UINT4 client_port, VALUE_PAIR *send) time_t start_time, dtime; char msg[4096]; int i; - SERVER *acctserver = rc_conf_srv("acctserver"); int timeout = rc_conf_int("radius_timeout"); int retries = rc_conf_int("radius_retries"); @@ -288,6 +314,24 @@ int rc_acct(UINT4 client_port, VALUE_PAIR *send) return result; } +/* + * Function: rc_acct + * + * Purpose: Builds an accounting request for port id client_port + * with the value_pairs send + * + * Remarks: NAS-IP-Address, NAS-Port and Acct-Delay-Time get filled + * in by this function, the rest has to be supplied. + */ + +int rc_acct(UINT4 client_port, VALUE_PAIR *send) +{ + SERVER *acctserver = rc_conf_srv("acctserver"); + if (!acctserver) return (ERROR_RC); + + return rc_acct_using_server(acctserver, client_port, send); +} + /* * Function: rc_acct_proxy * diff --git a/pppd/pppd.8 b/pppd/pppd.8 index e468994..229708a 100644 --- a/pppd/pppd.8 +++ b/pppd/pppd.8 @@ -1,5 +1,5 @@ .\" manual page [] for pppd 2.4 -.\" $Id: pppd.8,v 1.59 2002/01/11 18:04:37 etbe Exp $ +.\" $Id: pppd.8,v 1.60 2002/03/01 14:39:18 dfs Exp $ .\" SH section heading .\" SS subsection heading .\" LP paragraph @@ -853,6 +853,10 @@ to \fIname\fR. With this option, pppd will not agree to authenticate itself to the peer using CHAP. .TP +.B refuse-mschap +With this option, pppd will not agree to authenticate itself to the +peer using MS-CHAP. +.TP .B refuse-pap With this option, pppd will not agree to authenticate itself to the peer using PAP. @@ -861,6 +865,10 @@ peer using PAP. Require the peer to authenticate itself using CHAP [Challenge Handshake Authentication Protocol] authentication. .TP +.B require-mschap +Require the peer to authenticate itself using MS-CHAP [Microsft Challenge +Handshake Authentication Protocol] authentication. +.TP .B require-pap Require the peer to authenticate itself using PAP [Password Authentication Protocol] authentication. @@ -1012,7 +1020,7 @@ pppd will not agree to authenticate itself with a particular protocol if it has no secrets which could be used to do so. .LP Pppd stores secrets for use in authentication in secrets -files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for CHAP). +files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for CHAP/MS-CHAP). Both secrets files have the same format. The secrets files can contain secrets for pppd to use in authenticating itself to other systems, as well as secrets for pppd to use when authenticating other @@ -1500,7 +1508,7 @@ file should be owned by root and not readable or writable by any other user. Pppd will log a warning if this is not the case. .TP .B /etc/ppp/chap-secrets -Names, secrets and IP addresses for CHAP authentication. As for +Names, secrets and IP addresses for CHAP/MS-CHAP authentication. As for /etc/ppp/pap-secrets, this file should be owned by root and not readable or writable by any other user. Pppd will log a warning if this is not the case. diff --git a/pppd/pppd.h b/pppd/pppd.h index 5d287d6..d75a6fd 100644 --- a/pppd/pppd.h +++ b/pppd/pppd.h @@ -16,7 +16,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: pppd.h,v 1.65 2002/02/12 20:07:09 dfs Exp $ + * $Id: pppd.h,v 1.66 2002/03/01 14:39:18 dfs Exp $ */ /* @@ -86,6 +86,7 @@ typedef struct { const char *source; short int priority; short int winner; + void *addr3; } option_t; /* Values for flags */ @@ -102,6 +103,7 @@ typedef struct { #define OPT_ZEROOK 0x10000 /* 0 value is OK even if not within limits */ #define OPT_HIDE 0x10000 /* for o_string, print value as ?????? */ #define OPT_A2LIST 0x10000 /* for o_special, keep list of values */ +#define OPT_A2CLRB 0x10000 /* o_bool, clr val bits in *(u_char *)addr2 */ #define OPT_NOINCR 0x20000 /* value mustn't be increased */ #define OPT_ZEROINF 0x40000 /* with OPT_NOINCR, 0 == infinity */ #define OPT_PRIO 0x80000 /* process option priorities for this option */ @@ -117,6 +119,7 @@ typedef struct { #define OPT_A2PRINTER 0x10000000 /* *addr2 is a fn for printing option */ #define OPT_A2STRVAL 0x20000000 /* *addr2 points to current string value */ #define OPT_NOPRINT 0x40000000 /* don't print this option at all */ +#define OPT_A3OR 0x80000000 /* addr3 -> third location to rcv | value */ #define OPT_VAL(x) ((x) & OPT_VALUE) @@ -808,4 +811,8 @@ extern void (*snoop_send_hook) __P((unsigned char *p, int len)); #define MAX(a, b) ((a) > (b)? (a): (b)) #endif +#ifndef offsetof +#define offsetof(type, member) ((size_t) &((type *)0)->member) +#endif + #endif /* __PPP_H__ */