* chap.c - Crytographic Handshake Authentication Protocol.
* Copyright (c) 1991 Gregory M. Christy.
* 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.
chap_state chap
[NPPP
]; /* CHAP state; one for each unit */
static void ChapTimeout
__ARGS((caddr_t
));
static void ChapReceiveChallenge
__ARGS((chap_state
*, u_char
*, int, int));
static void ChapReceiveResponse
__ARGS((chap_state
*, u_char
*, int, int));
static void ChapReceiveSuccess
__ARGS((chap_state
*, u_char
*, int, int));
static void ChapReceiveFailure
__ARGS((chap_state
*, u_char
*, int, int));
static void ChapSendStatus
__ARGS((chap_state
*, int, int,
static void ChapSendChallenge
__ARGS((chap_state
*));
static void ChapSendResponse
__ARGS((chap_state
*, int, u_char
*, int));
static void ChapGenChallenge
__ARGS((int, u_char
*));
extern double drand48
__ARGS((void));
extern void srand48
__ARGS((long));
* ChapInit - Initialize a CHAP unit.
chap_state
*cstate
= &chap
[unit
];
cstate
->chal_str
[0] = '\000';
cstate
->clientstate
= CHAPCS_CLOSED
;
cstate
->serverstate
= CHAPSS_CLOSED
;
cstate
->timeouttime
= CHAP_DEFTIMEOUT
;
srand48((long) time(NULL
)); /* joggle random number generator */
* ChapAuthWithPeer - Authenticate us with our peer (start client).
chap_state
*cstate
= &chap
[unit
];
cstate
->flags
&= ~CHAPF_AWPPENDING
; /* Clear pending flag */
/* Protect against programming errors that compromise security */
if (cstate
->serverstate
!= CHAPSS_CLOSED
||
cstate
->flags
& CHAPF_APPENDING
) {
"ChapAuthWithPeer: we were called already!"))
if (cstate
->clientstate
== CHAPCS_CHALLENGE_SENT
|| /* should we be here? */
cstate
->clientstate
== CHAPCS_OPEN
)
if (!(cstate
->flags
& CHAPF_LOWERUP
)) {
cstate
->flags
|= CHAPF_AWPPENDING
; /* Nah, Wait */
ChapSendChallenge(cstate
); /* crank it up dude! */
TIMEOUT(ChapTimeout
, (caddr_t
) cstate
, cstate
->timeouttime
);
cstate
->clientstate
= CHAPCS_CHALLENGE_SENT
; /* update state */
* ChapAuthPeer - Authenticate our peer (start server).
chap_state
*cstate
= &chap
[unit
];
cstate
->flags
&= ~CHAPF_APPENDING
; /* Clear pending flag */
/* Already authenticat{ed,ing}? */
if (cstate
->serverstate
== CHAPSS_LISTEN
||
cstate
->serverstate
== CHAPSS_OPEN
)
if (!(cstate
->flags
& CHAPF_LOWERUP
)) {
cstate
->flags
|= CHAPF_APPENDING
; /* Wait for desired event */
cstate
->serverstate
= CHAPSS_LISTEN
;
* ChapTimeout - Timeout expired.
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
->clientstate
!= CHAPCS_CHALLENGE_SENT
)
ChapSendChallenge(cstate
); /* Send challenge */
TIMEOUT(ChapTimeout
, (caddr_t
) cstate
, cstate
->timeouttime
);
* ChapLowerUp - The lower layer is up.
* Start up if we have pending requests.
chap_state
*cstate
= &chap
[unit
];
cstate
->flags
|= CHAPF_LOWERUP
;
if (cstate
->flags
& CHAPF_AWPPENDING
) /* were we attempting authwithpeer? */
ChapAuthWithPeer(unit
); /* Try it now */
if (cstate
->flags
& CHAPF_APPENDING
) /* or authpeer? */
* ChapLowerDown - The lower layer is down.
chap_state
*cstate
= &chap
[unit
];
cstate
->flags
&= ~CHAPF_LOWERUP
;
if (cstate
->clientstate
== CHAPCS_CHALLENGE_SENT
) /* Timeout pending? */
UNTIMEOUT(ChapTimeout
, (caddr_t
) cstate
); /* Cancel timeout */
if (cstate
->serverstate
== CHAPSS_OPEN
) /* have we successfully authed? */
cstate
->clientstate
= CHAPCS_CLOSED
;
cstate
->serverstate
= CHAPSS_CLOSED
;
* ChapProtocolReject - Peer doesn't grok CHAP.
ChapLowerDown(unit
); /* shutdown chap */
/* Note: should we bail here if chap is required? */
* ChapInput - Input CHAP packet.
ChapInput(unit
, inpacket
, packet_len
)
chap_state
*cstate
= &chap
[unit
];
* Parse header (code, id and length).
* If packet too short, drop it.
if (packet_len
< CHAP_HEADERLEN
) {
CHAPDEBUG((LOG_INFO
, "ChapInput: rcvd short header."))
if (len
< CHAP_HEADERLEN
) {
CHAPDEBUG((LOG_INFO
, "ChapInput: rcvd illegal length."))
CHAPDEBUG((LOG_INFO
, "ChapInput: rcvd short packet."))
* Action depends on code.
ChapReceiveChallenge(cstate
, inp
, id
, len
);
ChapReceiveResponse(cstate
, inp
, id
, len
);
ChapReceiveFailure(cstate
, inp
, id
, len
);
ChapReceiveSuccess(cstate
, inp
, id
, len
);
default: /* Need code reject? */
syslog(LOG_WARNING
, "Unknown CHAP code (%d) received.", code
);
* ChapReceiveChallenge - Receive Challenge.
ChapReceiveChallenge(cstate
, inp
, id
, len
)
u_char secret
[MAX_SECRET_LEN
];
CHAPDEBUG((LOG_INFO
, "ChapReceiveChallenge: Rcvd id %d.", id
))
if (cstate
->serverstate
!= CHAPSS_LISTEN
) {
CHAPDEBUG((LOG_INFO
, "ChapReceiveChallenge: received challenge but not in listen state"))
CHAPDEBUG((LOG_INFO
, "ChapReceiveChallenge: rcvd short packet."))
GETCHAR(rchallenge_len
, inp
);
len
-= sizeof (u_char
) + rchallenge_len
;
CHAPDEBUG((LOG_INFO
, "ChapReceiveChallenge: rcvd short packet."))
INCPTR(rchallenge_len
, inp
);
BCOPY(inp
, rhostname
, len
);
CHAPDEBUG((LOG_INFO
, "ChapReceiveChallenge: received name field: %s",
GETSECRET(rhostname
, secret
, &secret_len
);/* get secret for specified host */
BCOPY(rchallenge
, buf
, rchallenge_len
); /* copy challenge into buffer */
BCOPY(secret
, buf
+ rchallenge_len
, secret_len
); /* append secret */
/* generate MD based on negotiated type */
switch (lcp_hisoptions
[cstate
->unit
].chap_mdtype
) {
case CHAP_DIGEST_MD5
: /* only MD5 is defined for now */
MD5Update(&mdContext
, buf
, rchallenge_len
+ secret_len
);
ChapSendResponse(cstate
, id
, &mdContext
.digest
[0], MD5_SIGNATURE_SIZE
);
CHAPDEBUG((LOG_INFO
, "unknown digest type %d",
lcp_hisoptions
[cstate
->unit
].chap_mdtype
))
* ChapReceiveResponse - Receive and process response.
ChapReceiveResponse(cstate
, inp
, id
, len
)
u_char
*remmd
, remmd_len
;
u_char secret
[MAX_SECRET_LEN
];
u_char chal_len
= cstate
->chal_len
;
CHAPDEBUG((LOG_INFO
, "ChapReceiveResponse: Rcvd id %d.", id
))
if (cstate
->clientstate
!= CHAPCS_CHALLENGE_SENT
) {
CHAPDEBUG((LOG_INFO
, "ChapReceiveResponse: received response but did not send a challenge"))
CHAPDEBUG((LOG_INFO
, "ChapReceiveResponse: rcvd short packet."))
GETCHAR(remmd_len
, inp
); /* get length of MD */
len
-= sizeof (u_char
) + remmd_len
;
CHAPDEBUG((LOG_INFO
, "ChapReceiveResponse: rcvd short packet."))
remmd
= inp
; /* get pointer to MD */
BCOPY(inp
, rhostname
, len
);
CHAPDEBUG((LOG_INFO
, "ChapReceiveResponse: received name field: %s",
GETSECRET(rhostname
, secret
, &secret_len
);/* get secret for specified host */
BCOPY(cstate
->chal_str
, buf
, chal_len
); /* copy challenge */
BCOPY(secret
, buf
+ chal_len
, secret_len
); /* append secret */
/* generate MD based on negotiated type */
switch (lcp_gotoptions
[cstate
->unit
].chap_mdtype
) {
case CHAP_DIGEST_MD5
: /* only MD5 is defined for now */
MD5Update(&mdContext
, buf
, chal_len
+ secret_len
);
/* compare local and remote MDs and send the appropriate status */
if (bcmp (&mdContext
.digest
[0], remmd
, MD5_SIGNATURE_SIZE
))
code
= CHAP_FAILURE
; /* they ain't the same */
code
= CHAP_SUCCESS
; /* they are the same! */
CHAPDEBUG((LOG_INFO
, "unknown digest type %d",
lcp_gotoptions
[cstate
->unit
].chap_mdtype
))
if (code
== CHAP_SUCCESS
)
sprintf((char *)msg
, "Welcome to %s.", hostname
);
sprintf((char *)msg
, "I don't like you. Go 'way.");
ChapSendStatus(cstate
, code
, id
, msg
, msglen
);
/* only crank up IPCP when either we aren't doing PAP, or if we are, */
/* that it is in open state */
if (code
== CHAP_SUCCESS
) {
cstate
->serverstate
= CHAPSS_OPEN
;
if (!lcp_hisoptions
[cstate
->unit
].neg_upap
||
(lcp_hisoptions
[cstate
->unit
].neg_upap
&&
upap
[cstate
->unit
].us_serverstate
== UPAPSS_OPEN
))
ipcp_activeopen(cstate
->unit
); /* Start IPCP */
* ChapReceiveSuccess - Receive Success
ChapReceiveSuccess(cstate
, inp
, id
, len
)
CHAPDEBUG((LOG_INFO
, "ChapReceiveSuccess: Rcvd id %d.", id
))
if (cstate
->clientstate
!= CHAPCS_CHALLENGE_SENT
) {
CHAPDEBUG((LOG_INFO
, "ChapReceiveSuccess: received success, but did not send a challenge."))
if (len
< sizeof (u_char
)) {
CHAPDEBUG((LOG_INFO
, "ChapReceiveSuccess: rcvd short packet."))
CHAPDEBUG((LOG_INFO
, "ChapReceiveSuccess: rcvd short packet."))
cstate
->clientstate
= CHAPCS_OPEN
;
/* only crank up IPCP when either we aren't doing PAP, or if we are, */
/* that it is in open state */
if (!lcp_gotoptions
[cstate
->unit
].neg_chap
||
(lcp_gotoptions
[cstate
->unit
].neg_chap
&&
upap
[cstate
->unit
].us_serverstate
== UPAPCS_OPEN
))
ipcp_activeopen(cstate
->unit
); /* Start IPCP */
* ChapReceiveFailure - Receive failure.
ChapReceiveFailure(cstate
, inp
, id
, len
)
CHAPDEBUG((LOG_INFO
, "ChapReceiveFailure: Rcvd id %d.", id
))
if (cstate
->clientstate
!= CHAPCS_CHALLENGE_SENT
) /* XXX */
if (len
< sizeof (u_char
)) {
CHAPDEBUG((LOG_INFO
, "ChapReceiveFailure: rcvd short packet."))
CHAPDEBUG((LOG_INFO
, "ChapReceiveFailure: rcvd short packet."))
cstate
->flags
&= ~CHAPF_UPVALID
; /* Clear valid flag */
cstate
->clientstate
= CHAPCS_CLOSED
; /* Pretend for a moment */
ChapAuthWithPeer(cstate
->unit
); /* Restart */
* ChapSendChallenge - Send an Authenticate challenge.
ChapSendChallenge(cstate
)
/* pick a random challenge length between MIN_CHALLENGE_LENGTH and
cstate
->chal_len
= (unsigned) ((drand48() *
(MAX_CHALLENGE_LENGTH
- MIN_CHALLENGE_LENGTH
)) +
chal_len
= cstate
->chal_len
;
outlen
= CHAP_HEADERLEN
+ 2 * sizeof (u_char
) + chal_len
+ hostname_len
;
MAKEHEADER(outp
, CHAP
); /* paste in a CHAP header */
PUTCHAR(CHAP_CHALLENGE
, outp
);
PUTCHAR(++cstate
->id
, outp
);
PUTCHAR(chal_len
, outp
); /* put length of challenge */
ChapGenChallenge(chal_len
, cstate
->chal_str
); /* generate a challenge string */
BCOPY(cstate
->chal_str
, outp
, chal_len
); /* copy it the the output buffer */
BCOPY(hostname
, outp
, hostname_len
); /* append hostname */
INCPTR(hostname_len
, outp
);
output(cstate
->unit
, outpacket_buf
, outlen
+ DLLHEADERLEN
);
CHAPDEBUG((LOG_INFO
, "ChapSendChallenge: Sent id %d.", cstate
->id
))
cstate
->clientstate
|= CHAPCS_CHALLENGE_SENT
;
* ChapSendStatus - Send a status response (ack or nak).
ChapSendStatus(cstate
, code
, id
, msg
, msglen
)
outlen
= CHAP_HEADERLEN
+ msglen
;
MAKEHEADER(outp
, CHAP
); /* paste in a header */
BCOPY(msg
, outp
, msglen
);
output(cstate
->unit
, outpacket_buf
, outlen
+ DLLHEADERLEN
);
CHAPDEBUG((LOG_INFO
, "ChapSendStatus: Sent code %d, id %d.", code
, id
))
* ChapGenChallenge is used to generate a pseudo-random challenge string of
* a pseudo-random length between min_len and max_len and return the
* challenge string, and the message digest of the secret appended to
* the challenge string. the message digest type is specified by mdtype.
* It returns with the string in the caller-supplied buffer str (which
* should be instantiated with a length of max_len + 1), and the
* length of the generated string into chal_len.
ChapGenChallenge(chal_len
, str
)
/* generate a random string */
for (i
= 0; i
< chal_len
; i
++ )
*ptr
++ = (char) (drand48() * 0xff);
*ptr
= 0; /* null terminate it so we can printf it */
* ChapSendResponse - send a response packet with the message
* digest specified by md and md_len
ChapSendResponse(cstate
, id
, md
, md_len
)
outlen
= CHAP_HEADERLEN
+ sizeof (u_char
) + md_len
+ hostname_len
;
PUTCHAR(CHAP_RESPONSE
, outp
); /* we are a response */
PUTCHAR(id
, outp
); /* copy id from challenge packet */
PUTSHORT(outlen
, outp
); /* packet length */
PUTCHAR(md_len
, outp
); /* length of MD */
BCOPY(md
, outp
, md_len
); /* copy MD to buffer */
BCOPY(hostname
, outp
, hostname_len
); /* append hostname */
INCPTR(hostname_len
, outp
);
output(cstate
->unit
, outpacket_buf
, outlen
+ DLLHEADERLEN
); /* bomb's away! */
return (double)random() / (double)0x7fffffffL
; /* 2**31-1 */