/*
* chap.c - Challenge Handshake Authentication Protocol.
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 1993 The Australian National University.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the Australian National University. The name of the University
* may not be used to endorse or promote products derived from this
* software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Copyright (c) 1991 Gregory M. Christy.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by Gregory M. Christy. The name of the author may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#define RCSID "$Id: chap.c,v 1.24 1999/11/15 01:51:50 paulus Exp $"
/*
* TODO:
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include "pppd.h"
#include "chap.h"
#include "md5.h"
#if defined(CHAPMS) || defined(CHAPMSV2)
#include "chap_ms.h"
#endif
#if !defined(lint) && !defined(_lint)
static const char rcsid[] = RCSID;
#endif
/*
* Command-line options.
*/
static option_t chap_option_list[] = {
{ "chap-restart", o_int, &chap[0].timeouttime,
"Set CHAP Challenge retry interval" },
{ "chap-max-challenge", o_int, &chap[0].max_transmits,
"Max number of Challenges sent without a valid response" },
{ "chap-interval", o_int, &chap[0].chal_interval,
"Set interval for rechallenge" },
#if defined(CHAPMS) && defined(MSLANMAN)
{ "ms-lanman", o_bool, &ms_lanman,
"Use obsolete LAN Manager password when using MS-CHAP", 1 },
#endif
{ NULL }
};
/*
* Protocol entry points.
*/
static void ChapInit __P((int));
static void ChapLowerUp __P((int));
static void ChapLowerDown __P((int));
static void ChapInput __P((int, u_char *, int));
static void ChapProtocolReject __P((int));
static int ChapPrintPkt __P((u_char *, int,
void (*) __P((void *, const char *, ...)), void *));
struct protent chap_protent = {
PPP_CHAP, /* PPP protocol number */
ChapInit, /* Initialization procedure */
ChapInput, /* Process a received packet */
ChapProtocolReject, /* Process a received protocol-reject */
ChapLowerUp, /* Lower layer has come up */
ChapLowerDown, /* Lower layer has gone down */
NULL, /* Open the protocol */
NULL, /* Close the protocol */
ChapPrintPkt, /* Print a packet in readable form */
NULL, /* Process a received data packet */
1, /* 0 iff protocol is disabled */
"CHAP", /* Text name of protocol */
NULL, /* Text name of corresponding data protocol */
chap_option_list, /* List of command-line options */
NULL, /* Check requested options, assign defaults */
NULL, /* Configure interface for demand-dial */
NULL /* Say whether to bring up link for this pkt */
};
/* Not static'd for plug-ins */
chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
static void ChapChallengeTimeout __P((void *));
static void ChapResponseTimeout __P((void *));
static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
static void ChapRechallenge __P((void *));
static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
static void ChapSendStatus __P((chap_state *, int));
static void ChapSendChallenge __P((chap_state *));
static void ChapSendResponse __P((chap_state *));
static void ChapGenChallenge __P((chap_state *));
static const char *
chap_cstate(int clientstate)
{
static const char *cstate[] = { CHAPCS__LIST };
static char buf[32];
if (clientstate < 0 || clientstate >= Dim(cstate)) {
(void) slprintf(buf, sizeof(buf), "#%d", clientstate);
return (buf);
}
return cstate[clientstate];
}
static const char *
chap_sstate(int serverstate)
{
static const char *sstate[] = { CHAPSS__LIST };
static char buf[32];
if (serverstate < 0 || serverstate >= Dim(sstate)) {
(void) slprintf(buf, sizeof(buf), "#%d", serverstate);
return (buf);
}
return sstate[serverstate];
}
/*
* ChapInit - Initialize a CHAP unit.
*/
static void
ChapInit(unit)
int unit;
{
chap_state *cstate = &chap[unit];
BZERO(cstate, sizeof(*cstate));
cstate->unit = unit;
cstate->clientstate = CHAPCS_INITIAL;
cstate->serverstate = CHAPSS_INITIAL;
cstate->timeouttime = CHAP_DEFTIMEOUT;
cstate->max_transmits = CHAP_DEFTRANSMITS;
/* XXX save get_first_hwaddr() here. */
/* random number generator is initialized in magic_init */
}
/*
* ChapAuthWithPeer - Authenticate us with our peer (start client).
*
*/
void
ChapAuthWithPeer(unit, our_name, digest)
int unit;
char *our_name;
int digest;
{
chap_state *cstate = &chap[unit];
cstate->resp_name = our_name;
cstate->resp_type = digest;
if (cstate->clientstate == CHAPCS_INITIAL ||
cstate->clientstate == CHAPCS_PENDING) {
/* lower layer isn't up - wait until later */
cstate->clientstate = CHAPCS_PENDING;
return;
}
/*
* We get here as a result of LCP coming up.
* So even if CHAP was open before, we will
* have to re-authenticate ourselves.
*/
cstate->clientstate = CHAPCS_LISTEN;
}
/*
* ChapAuthPeer - Authenticate our peer (start server).
*/
void
ChapAuthPeer(unit, our_name, digest)
int unit;
char *our_name;
int digest;
{
chap_state *cstate = &chap[unit];
cstate->chal_name = our_name;
cstate->chal_type = digest;
if (cstate->serverstate == CHAPSS_INITIAL ||
cstate->serverstate == CHAPSS_PENDING) {
/* lower layer isn't up - wait until later */
cstate->serverstate = CHAPSS_PENDING;
return;
}
ChapGenChallenge(cstate);
ChapSendChallenge(cstate); /* crank it up dude! */
cstate->serverstate = CHAPSS_INITIAL_CHAL;
}
/*
* ChapChallengeTimeout - Timeout expired on sending challenge.
*/
static void
ChapChallengeTimeout(arg)
void *arg;
{
chap_state *cstate = (chap_state *) arg;
/* if we aren't sending challenges, don't worry. then again we */
/* probably shouldn't be here either */
if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
cstate->serverstate != CHAPSS_RECHALLENGE)
return;
if (cstate->chal_transmits >= cstate->max_transmits) {
/* give up on peer */
error("Peer failed to respond to CHAP challenge");
cstate->serverstate = CHAPSS_BADAUTH;
auth_peer_fail(cstate->unit, PPP_CHAP);
return;
}
ChapSendChallenge(cstate); /* Re-send challenge */
}
/*
* ChapResponseTimeout - Timeout expired on sending response.
*/
static void
ChapResponseTimeout(arg)
void *arg;
{
chap_state *cstate = (chap_state *) arg;
/* if we aren't sending a response, don't worry. */
if (cstate->clientstate != CHAPCS_RESPONSE)
return;
ChapSendResponse(cstate); /* re-send response */
}
/*
* ChapRechallenge - Time to challenge the peer again.
*/
static void
ChapRechallenge(arg)
void *arg;
{
chap_state *cstate = (chap_state *) arg;
/* if we aren't sending a response, don't worry. */
if (cstate->serverstate != CHAPSS_OPEN)
return;
ChapGenChallenge(cstate);
ChapSendChallenge(cstate);
cstate->serverstate = CHAPSS_RECHALLENGE;
}
/*
* ChapLowerUp - The lower layer is up.
*
* Start up if we have pending requests.
*/
static void
ChapLowerUp(unit)
int unit;
{
chap_state *cstate = &chap[unit];
if (cstate->clientstate == CHAPCS_INITIAL)
cstate->clientstate = CHAPCS_CLOSED;
else if (cstate->clientstate == CHAPCS_PENDING)
cstate->clientstate = CHAPCS_LISTEN;
if (cstate->serverstate == CHAPSS_INITIAL)
cstate->serverstate = CHAPSS_CLOSED;
else if (cstate->serverstate == CHAPSS_PENDING) {
ChapGenChallenge(cstate);
ChapSendChallenge(cstate);
cstate->serverstate = CHAPSS_INITIAL_CHAL;
}
}
/*
* ChapLowerDown - The lower layer is down.
*
* Cancel all timeouts.
*/
static void
ChapLowerDown(unit)
int unit;
{
chap_state *cstate = &chap[unit];
/* Timeout(s) pending? Cancel if so. */
if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
cstate->serverstate == CHAPSS_RECHALLENGE)
UNTIMEOUT(ChapChallengeTimeout, cstate);
else if (cstate->serverstate == CHAPSS_OPEN
&& cstate->chal_interval != 0)
UNTIMEOUT(ChapRechallenge, cstate);
if (cstate->clientstate == CHAPCS_RESPONSE)
UNTIMEOUT(ChapResponseTimeout, cstate);
cstate->clientstate = CHAPCS_INITIAL;
cstate->serverstate = CHAPSS_INITIAL;
}
/*
* ChapProtocolReject - Peer doesn't grok CHAP.
*/
static void
ChapProtocolReject(unit)
int unit;
{
chap_state *cstate = &chap[unit];
if (cstate->serverstate != CHAPSS_INITIAL &&
cstate->serverstate != CHAPSS_CLOSED)
auth_peer_fail(unit, PPP_CHAP);
if (cstate->clientstate != CHAPCS_INITIAL &&
cstate->clientstate != CHAPCS_CLOSED)
auth_withpeer_fail(unit, PPP_CHAP);
ChapLowerDown(unit); /* shutdown chap */
}
/*
* ChapInput - Input CHAP packet.
*/
static void
ChapInput(unit, inpacket, packet_len)
int unit;
u_char *inpacket;
int packet_len;
{
chap_state *cstate = &chap[unit];
u_char *inp;
u_char code, id;
int len;
/*
* Parse header (code, id and length).
* If packet too short, drop it.
*/
inp = inpacket;
if (packet_len < CHAP_HEADERLEN) {
error("CHAP: packet is too small (%d < %d)", packet_len,
CHAP_HEADERLEN);
return;
}
GETCHAR(code, inp);
GETCHAR(id, inp);
GETSHORT(len, inp);
if (len < CHAP_HEADERLEN || len > packet_len) {
error("CHAP: packet has illegal length %d (%d..%d)",
len, CHAP_HEADERLEN, packet_len);
return;
}
len -= CHAP_HEADERLEN;
/*
* Action depends on code (as in fact it usually does :-).
*/
switch (code) {
case CHAP_CHALLENGE:
ChapReceiveChallenge(cstate, inp, id, len);
break;
case CHAP_RESPONSE:
ChapReceiveResponse(cstate, inp, id, len);
break;
case CHAP_FAILURE:
ChapReceiveFailure(cstate, inp, id, len);
break;
case CHAP_SUCCESS:
ChapReceiveSuccess(cstate, inp, id, len);
break;
default:
/* CHAP does not define a code reject. */
warn("Unknown CHAP code (%d) received.", code);
break;
}
}
/*
* ChapReceiveChallenge - Receive Challenge and send Response.
*/
static void
ChapReceiveChallenge(cstate, inp, id, len)
chap_state *cstate;
u_char *inp;
int id;
int len;
{
int rchallenge_len;
u_char *rchallenge;
int secret_len;
char rhostname[MAXNAMELEN];
char secret[MAXSECRETLEN];
MD5_CTX mdContext;
u_char hash[MD5_SIGNATURE_SIZE];
int fake_response = 0;
if (cstate->clientstate == CHAPCS_CLOSED ||
cstate->clientstate == CHAPCS_PENDING) {
if (debug)
dbglog("CHAP Challenge unexpectedly received in state %s",
chap_cstate(cstate->clientstate));
return;
}
if (len < 2) {
error("CHAP: Challenge message length %d", len);
return;
}
GETCHAR(rchallenge_len, inp);
len -= sizeof (u_char) + rchallenge_len; /* now name field length */
if (len < 0) {
error("CHAP: Challenge truncated");
return;
}
rchallenge = inp;
INCPTR(rchallenge_len, inp);
if (len >= sizeof(rhostname))
len = sizeof(rhostname) - 1;
if (len > 0) {
BCOPY(inp, rhostname, len);
}
rhostname[len] = '\0';
#ifdef CHECK_CHALLENGE_LENGTH
if (rchallenge_len < CHECK_CHALLENGE_LENGTH) {
warn("CHAP: Challenge from %s unreasonably short (%d octets)",
rhostname, rchallenge_len);
fake_response = 1;
}
#endif
/* XXX compare against saved get_first_hwaddr() here. */
/* Microsoft NT doesn't send a name in the CHAP Challenge message */
if (explicit_remote ||
(remote_name[0] != '\0' && rhostname[0] == '\0')) {
(void) strlcpy(rhostname, remote_name, sizeof(rhostname));
if (debug)
dbglog("CHAP: Peer gave no name; using '%q' as remote name",
rhostname);
}
if (cstate->peercname[0] == '\0') {
(void) strlcpy(cstate->peercname, rhostname,
sizeof (cstate->peercname));
} else if (strcmp(rhostname, cstate->peercname) != 0) {
if (++cstate->rename_count == 1) {
info("CHAP: peer challenge name changed from '%q' to '%q'",
cstate->peercname, rhostname);
(void) strlcpy(cstate->peercname, rhostname,
sizeof (cstate->peercname));
} else {
fake_response = 1;
if (cstate->rename_count == 2)
warn("CHAP: peer challenge name changed again to '%q'",
rhostname);
}
}
/* get secret for authenticating ourselves with the specified host */
if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
secret, &secret_len, 0)) {
secret_len = 0; /* assume null secret if can't find one */
warn("No CHAP secret found for authenticating us (%q) to %q",
cstate->resp_name, rhostname);
}
/* cancel response send timeout if necessary */
if (cstate->clientstate == CHAPCS_RESPONSE)
UNTIMEOUT(ChapResponseTimeout, cstate);
cstate->resp_id = id;
cstate->resp_transmits = 0;
/* generate MD based on negotiated type */
switch (cstate->resp_type) {
case CHAP_DIGEST_MD5:
MD5Init(&mdContext);
MD5Update(&mdContext, &cstate->resp_id, 1);
MD5Update(&mdContext, (u_char *)secret, (unsigned)secret_len);
MD5Update(&mdContext, rchallenge, rchallenge_len);
MD5Final(hash, &mdContext);
if (fake_response) {
for (len = 0; len < MD5_SIGNATURE_SIZE; len++)
cstate->response[len] = (char) (drand48() * 0xff);
} else {
BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
}
cstate->resp_length = MD5_SIGNATURE_SIZE;
break;
#ifdef CHAPMS
case CHAP_MICROSOFT:
ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
break;
#endif
#ifdef CHAPMSV2
case CHAP_MICROSOFT_V2:
ChapMSv2(cstate, rchallenge, rchallenge_len, secret, secret_len);
break;
#endif
default:
error("CHAP: unknown digest type %d", cstate->resp_type);
return;
}
BZERO(secret, sizeof(secret));
ChapSendResponse(cstate);
}
/*
* ChapReceiveResponse - Receive and process response.
*/
static void
ChapReceiveResponse(cstate, inp, id, len)
chap_state *cstate;
u_char *inp;
int id;
int len;
{
u_char *remmd, remmd_len;
int secret_len, old_state;
int code;
char rhostname[MAXNAMELEN], *rhn;
MD5_CTX mdContext;
char secret[MAXSECRETLEN];
u_char hash[MD5_SIGNATURE_SIZE];
if (cstate->serverstate == CHAPSS_CLOSED ||
cstate->serverstate == CHAPSS_PENDING) {
if (debug)
dbglog("CHAP Response unexpectedly received in state %s",
chap_sstate(cstate->serverstate));
return;
}
if (id != cstate->chal_id) {
if (debug)
dbglog("CHAP: discard response %d; expecting %d", id,
cstate->chal_id);
return; /* doesn't match ID of last challenge */
}
/*
* If we have received a duplicate or bogus Response,
* we have to send the same answer (Success/Failure)
* as we did for the first Response we saw.
*/
if (cstate->serverstate == CHAPSS_OPEN) {
if (debug)
dbglog("CHAP ignoring response and resending success message");
ChapSendStatus(cstate, CHAP_SUCCESS);
return;
}
if (cstate->serverstate == CHAPSS_BADAUTH) {
if (debug)
dbglog("CHAP ignoring response and resending failure message");
ChapSendStatus(cstate, CHAP_FAILURE);
return;
}
if (len < 2) {
error("CHAP: Response message length %d", len);
return;
}
GETCHAR(remmd_len, inp); /* get length of MD */
remmd = inp; /* get pointer to MD */
INCPTR(remmd_len, inp);
len -= sizeof (u_char) + remmd_len;
if (len < 0) {
error("CHAP: Response truncated");
return;
}
UNTIMEOUT(ChapChallengeTimeout, cstate);
if (len >= sizeof(rhostname))
len = sizeof(rhostname) - 1;
BCOPY(inp, rhostname, len);
rhostname[len] = '\0';
/*
* Get secret for authenticating them with us,
* do the hash ourselves, and compare the result.
*/
code = CHAP_FAILURE;
rhn = (explicit_remote? remote_name: rhostname);
if (cstate->serverstate == CHAPSS_RECHALLENGE &&
strcmp(rhostname, peer_authname) != 0) {
warn("Peer changed his name from '%q' to '%q' on rechallenge",
peer_authname, rhostname);
} else if (!get_secret(cstate->unit, rhn, cstate->chal_name, secret,
&secret_len, 1)) {
warn("No CHAP secret found for authenticating %q to us (%q)",
rhn, cstate->chal_name);
} else {
/* generate MD based on negotiated type */
switch (cstate->chal_type) {
case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
if (remmd_len != MD5_SIGNATURE_SIZE)
break; /* it's not even the right length */
MD5Init(&mdContext);
MD5Update(&mdContext, &cstate->chal_id, 1);
MD5Update(&mdContext, (u_char *)secret, secret_len);
MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
MD5Final(hash, &mdContext);
/* compare local and remote 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:
if (ChapMSValidate(cstate, remmd, remmd_len, secret,
secret_len))
code = CHAP_SUCCESS;
break;
#endif
#ifdef CHAPMSV2
case CHAP_MICROSOFT_V2:
if (ChapMSv2Validate(cstate, rhostname, remmd, remmd_len,
secret, secret_len))
code = CHAP_SUCCESS;
break;
#endif
default:
error("CHAP: unknown digest type %d", cstate->chal_type);
}
}
BZERO(secret, sizeof(secret));
ChapSendStatus(cstate, code);
if (code == CHAP_SUCCESS) {
old_state = cstate->serverstate;
cstate->serverstate = CHAPSS_OPEN;
if (old_state == CHAPSS_INITIAL_CHAL) {
auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
}
if (cstate->chal_interval != 0)
TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
if (old_state == CHAPSS_INITIAL_CHAL)
notice("CHAP peer authentication succeeded for %q", rhostname);
else if (debug)
dbglog("CHAP peer reauthentication succeeded for %q", rhostname);
} else {
error("CHAP peer %sauthentication failed for remote host %q",
(cstate->serverstate == CHAPSS_INITIAL_CHAL ? "" : "re"),
rhostname);
cstate->serverstate = CHAPSS_BADAUTH;
auth_peer_fail(cstate->unit, PPP_CHAP);
}
}
/*
* ChapReceiveSuccess - Receive Success
*/
/*ARGSUSED*/
static void
ChapReceiveSuccess(cstate, inp, id, len)
chap_state *cstate;
u_char *inp;
u_char id;
int len;
{
if (cstate->clientstate == CHAPCS_OPEN) {
/* presumably an answer to a duplicate response */
if (debug)
dbglog("CHAP duplicate Success message ignored");
return;
}
if (cstate->clientstate != CHAPCS_RESPONSE) {
/* don't know what this is */
if (debug)
dbglog("CHAP Success unexpectedly received in state %s",
chap_cstate(cstate->clientstate));
return;
}
UNTIMEOUT(ChapResponseTimeout, cstate);
/*
* Print message.
*/
if (len > 0)
PRINTMSG(inp, len);
cstate->clientstate = CHAPCS_OPEN;
auth_withpeer_success(cstate->unit, PPP_CHAP);
}
/*
* ChapReceiveFailure - Receive failure.
*/
/*ARGSUSED*/
static void
ChapReceiveFailure(cstate, inp, id, len)
chap_state *cstate;
u_char *inp;
u_char id;
int len;
{
if (cstate->clientstate != CHAPCS_RESPONSE) {
/* don't know what this is */
if (debug)
dbglog("CHAP Failure unexpectedly received in state %s",
chap_cstate(cstate->clientstate));
return;
}
UNTIMEOUT(ChapResponseTimeout, cstate);
/*
* Print message.
*/
if (len > 0)
PRINTMSG(inp, len);
error("CHAP authentication failed");
auth_withpeer_fail(cstate->unit, PPP_CHAP);
}
/*
* ChapSendChallenge - Send an Authenticate challenge.
*/
static void
ChapSendChallenge(cstate)
chap_state *cstate;
{
u_char *outp;
int chal_len, name_len;
int outlen;
chal_len = cstate->chal_len;
name_len = strlen(cstate->chal_name);
outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
outp = outpacket_buf;
MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
PUTCHAR(CHAP_CHALLENGE, outp);
PUTCHAR(cstate->chal_id, outp);
PUTSHORT(outlen, outp);
PUTCHAR(chal_len, outp); /* put length of challenge */
BCOPY(cstate->challenge, outp, chal_len);
INCPTR(chal_len, outp);
BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
++cstate->chal_transmits;
}
/*
* ChapSendStatus - Send a status response (ack or nak).
*/
static void
ChapSendStatus(cstate, code)
chap_state *cstate;
int code;
{
u_char *outp;
int outlen, msglen;
char msg[256], *msgp;
if ((msgp = cstate->stat_message) != NULL) {
msglen = cstate->stat_length;
} else {
if (code == CHAP_SUCCESS)
(void) slprintf(msg, sizeof(msg), "Welcome to %s.", hostname);
else
(void) slprintf(msg, sizeof(msg), "I don't like you. Go 'way.");
msglen = strlen(msg);
msgp = msg;
}
outlen = CHAP_HEADERLEN + msglen;
outp = outpacket_buf;
MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
PUTCHAR(code, outp);
PUTCHAR(cstate->chal_id, outp);
PUTSHORT(outlen, outp);
BCOPY(msgp, outp, msglen);
output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
}
/*
* ChapGenChallenge is used to generate a pseudo-random challenge string of
* a pseudo-random length between min_len and max_len. The challenge
* string and its length are stored in *cstate, and various other fields of
* *cstate are initialized.
*/
static void
ChapGenChallenge(cstate)
chap_state *cstate;
{
int chal_len;
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);
cstate->chal_len = chal_len;
cstate->chal_id = ++cstate->id;
cstate->chal_transmits = 0;
/* XXX tack on saved get_first_hwaddr() here. */
/* generate a random string */
for (i = 0; i < chal_len; i++)
*ptr++ = (char) (drand48() * 0xff);
}
/*
* ChapSendResponse - send a response packet with values as specified
* in *cstate.
*/
/* ARGSUSED */
static void
ChapSendResponse(cstate)
chap_state *cstate;
{
u_char *outp;
int outlen, md_len, name_len;
md_len = cstate->resp_length;
name_len = strlen(cstate->resp_name);
outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
outp = outpacket_buf;
MAKEHEADER(outp, PPP_CHAP);
PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
PUTSHORT(outlen, outp); /* packet length */
PUTCHAR(md_len, outp); /* length of MD */
BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
INCPTR(md_len, outp);
BCOPY(cstate->resp_name, outp, name_len); /* append our name */
/* send the packet */
output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
cstate->clientstate = CHAPCS_RESPONSE;
TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
++cstate->resp_transmits;
}
/*
* ChapPrintPkt - print the contents of a CHAP packet.
*/
static char *ChapCodenames[] = {
"Challenge", "Response", "Success", "Failure"
};
static int
ChapPrintPkt(p, plen, printer, arg)
u_char *p;
int plen;
void (*printer) __P((void *, const char *, ...));
void *arg;
{
int code, id, len;
int clen, nlen;
u_char x;
if (plen < CHAP_HEADERLEN)
return 0;
GETCHAR(code, p);
GETCHAR(id, p);
GETSHORT(len, p);
if (len < CHAP_HEADERLEN || len > plen)
return 0;
if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
printer(arg, " %s", ChapCodenames[code-1]);
else
printer(arg, " code=0x%x", code);
printer(arg, " id=0x%x", id);
len -= CHAP_HEADERLEN;
switch (code) {
case CHAP_CHALLENGE:
case CHAP_RESPONSE:
if (len < 1)
break;
clen = p[0];
if (len < clen + 1)
break;
++p;
nlen = len - clen - 1;
printer(arg, " <");
for (; clen > 0; --clen) {
GETCHAR(x, p);
printer(arg, "%.2x", x);
}
printer(arg, ">, name = ");
print_string((char *)p, nlen, printer, arg);
break;
case CHAP_FAILURE:
case CHAP_SUCCESS:
printer(arg, " ");
print_string((char *)p, len, printer, arg);
break;
default:
for (clen = len; clen > 0; --clen) {
GETCHAR(x, p);
printer(arg, " %.2x", x);
}
}
return len + CHAP_HEADERLEN;
}