2 * chap.c - Crytographic Handshake Authentication Protocol.
4 * Copyright (c) 1991 Gregory M. Christy.
7 * Redistribution and use in source and binary forms are permitted
8 * provided that the above copyright notice and this paragraph are
9 * duplicated in all such forms and that any documentation,
10 * advertising materials, and other materials related to such
11 * distribution and use acknowledge that the software was developed
12 * by Gregory M. Christy. The name of the author may not be used to
13 * endorse or promote products derived from this software without
14 * specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 static char rcsid[] = "$Id: chap.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
30 #include <sys/types.h>
39 chap_state chap[NPPP]; /* CHAP state; one for each unit */
41 static void ChapChallengeTimeout __ARGS((caddr_t));
42 static void ChapResponseTimeout __ARGS((caddr_t));
43 static void ChapReceiveChallenge __ARGS((chap_state *, u_char *, int, int));
44 static void ChapReceiveResponse __ARGS((chap_state *, u_char *, int, int));
45 static void ChapReceiveSuccess __ARGS((chap_state *, u_char *, int, int));
46 static void ChapReceiveFailure __ARGS((chap_state *, u_char *, int, int));
47 static void ChapSendStatus __ARGS((chap_state *, int));
48 static void ChapSendChallenge __ARGS((chap_state *));
49 static void ChapSendResponse __ARGS((chap_state *));
50 static void ChapGenChallenge __ARGS((chap_state *));
52 extern double drand48 __ARGS((void));
53 extern void srand48 __ARGS((long));
56 * ChapInit - Initialize a CHAP unit.
62 chap_state *cstate = &chap[unit];
64 BZERO(cstate, sizeof(*cstate));
66 cstate->clientstate = CHAPCS_INITIAL;
67 cstate->serverstate = CHAPSS_INITIAL;
68 cstate->timeouttime = CHAP_DEFTIMEOUT;
69 cstate->max_transmits = CHAP_DEFTRANSMITS;
70 srand48((long) time(NULL)); /* joggle random number generator */
75 * ChapAuthWithPeer - Authenticate us with our peer (start client).
79 ChapAuthWithPeer(unit, our_name, digest)
84 chap_state *cstate = &chap[unit];
86 cstate->resp_name = our_name;
87 cstate->resp_type = digest;
89 if (cstate->clientstate == CHAPCS_INITIAL ||
90 cstate->clientstate == CHAPCS_PENDING) {
91 /* lower layer isn't up - wait until later */
92 cstate->clientstate = CHAPCS_PENDING;
97 * We get here as a result of LCP coming up.
98 * So even if CHAP was open before, we will
99 * have to re-authenticate ourselves.
101 cstate->clientstate = CHAPCS_LISTEN;
106 * ChapAuthPeer - Authenticate our peer (start server).
109 ChapAuthPeer(unit, our_name, digest)
114 chap_state *cstate = &chap[unit];
116 cstate->chal_name = our_name;
117 cstate->chal_type = digest;
119 if (cstate->serverstate == CHAPSS_INITIAL ||
120 cstate->serverstate == CHAPSS_PENDING) {
121 /* lower layer isn't up - wait until later */
122 cstate->serverstate = CHAPSS_PENDING;
126 ChapGenChallenge(cstate);
127 ChapSendChallenge(cstate); /* crank it up dude! */
128 cstate->serverstate = CHAPSS_INITIAL_CHAL;
133 * ChapChallengeTimeout - Timeout expired on sending challenge.
136 ChapChallengeTimeout(arg)
139 chap_state *cstate = (chap_state *) arg;
141 /* if we aren't sending challenges, don't worry. then again we */
142 /* probably shouldn't be here either */
143 if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
144 cstate->serverstate != CHAPSS_RECHALLENGE)
147 if (cstate->chal_transmits >= cstate->max_transmits) {
148 /* give up on peer */
149 syslog(LOG_ERR, "Peer failed to respond to CHAP challenge");
150 cstate->serverstate = CHAPSS_BADAUTH;
151 auth_peer_fail(cstate->unit, CHAP);
155 ChapSendChallenge(cstate); /* Re-send challenge */
160 * ChapResponseTimeout - Timeout expired on sending response.
163 ChapResponseTimeout(arg)
166 chap_state *cstate = (chap_state *) arg;
168 /* if we aren't sending a response, don't worry. */
169 if (cstate->clientstate != CHAPCS_RESPONSE)
172 ChapSendResponse(cstate); /* re-send response */
177 * ChapRechallenge - Time to challenge the peer again.
183 chap_state *cstate = (chap_state *) arg;
185 /* if we aren't sending a response, don't worry. */
186 if (cstate->serverstate != CHAPSS_OPEN)
189 ChapGenChallenge(cstate);
190 ChapSendChallenge(cstate);
191 cstate->serverstate = CHAPSS_RECHALLENGE;
193 if (cstate->chal_interval != 0)
194 TIMEOUT(ChapRechallenge, (caddr_t) cstate, cstate->chal_interval);
199 * ChapLowerUp - The lower layer is up.
201 * Start up if we have pending requests.
207 chap_state *cstate = &chap[unit];
209 if (cstate->clientstate == CHAPCS_INITIAL)
210 cstate->clientstate = CHAPCS_CLOSED;
211 else if (cstate->clientstate == CHAPCS_PENDING)
212 cstate->clientstate = CHAPCS_LISTEN;
214 if (cstate->serverstate == CHAPSS_INITIAL)
215 cstate->serverstate = CHAPSS_CLOSED;
216 else if (cstate->serverstate == CHAPSS_PENDING) {
217 ChapGenChallenge(cstate);
218 ChapSendChallenge(cstate);
219 cstate->serverstate = CHAPSS_INITIAL_CHAL;
225 * ChapLowerDown - The lower layer is down.
227 * Cancel all timeouts.
233 chap_state *cstate = &chap[unit];
235 /* Timeout(s) pending? Cancel if so. */
236 if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
237 cstate->serverstate == CHAPSS_RECHALLENGE)
238 UNTIMEOUT(ChapChallengeTimeout, (caddr_t) cstate);
239 else if (cstate->serverstate == CHAPSS_OPEN
240 && cstate->chal_interval != 0)
241 UNTIMEOUT(ChapRechallenge, (caddr_t) cstate);
242 if (cstate->clientstate == CHAPCS_RESPONSE)
243 UNTIMEOUT(ChapResponseTimeout, (caddr_t) cstate);
245 cstate->clientstate = CHAPCS_INITIAL;
246 cstate->serverstate = CHAPSS_INITIAL;
251 * ChapProtocolReject - Peer doesn't grok CHAP.
254 ChapProtocolReject(unit)
257 chap_state *cstate = &chap[unit];
259 if (cstate->serverstate != CHAPSS_INITIAL &&
260 cstate->serverstate != CHAPSS_CLOSED)
261 auth_peer_fail(unit, CHAP);
262 if (cstate->clientstate != CHAPCS_INITIAL &&
263 cstate->clientstate != CHAPCS_CLOSED)
264 auth_withpeer_fail(unit, CHAP);
265 ChapLowerDown(unit); /* shutdown chap */
270 * ChapInput - Input CHAP packet.
273 ChapInput(unit, inpacket, packet_len)
278 chap_state *cstate = &chap[unit];
284 * Parse header (code, id and length).
285 * If packet too short, drop it.
288 if (packet_len < CHAP_HEADERLEN) {
289 CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header."));
295 if (len < CHAP_HEADERLEN) {
296 CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length."));
299 if (len > packet_len) {
300 CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet."));
303 len -= CHAP_HEADERLEN;
306 * Action depends on code (as in fact it usually does :-).
310 ChapReceiveChallenge(cstate, inp, id, len);
314 ChapReceiveResponse(cstate, inp, id, len);
318 ChapReceiveFailure(cstate, inp, id, len);
322 ChapReceiveSuccess(cstate, inp, id, len);
325 default: /* Need code reject? */
326 syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code);
333 * ChapReceiveChallenge - Receive Challenge and send Response.
336 ChapReceiveChallenge(cstate, inp, id, len)
345 char secret[MAXSECRETLEN];
349 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id));
350 if (cstate->clientstate == CHAPCS_CLOSED ||
351 cstate->clientstate == CHAPCS_PENDING) {
352 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d",
353 cstate->clientstate));
358 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
362 GETCHAR(rchallenge_len, inp);
363 len -= sizeof (u_char) + rchallenge_len; /* now name field length */
365 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
369 INCPTR(rchallenge_len, inp);
371 if (len >= sizeof(rhostname))
372 len = sizeof(rhostname) - 1;
373 BCOPY(inp, rhostname, len);
374 rhostname[len] = '\000';
376 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field: %s",
379 /* get secret for authenticating ourselves with the specified host */
380 if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
381 secret, &secret_len, 0)) {
382 secret_len = 0; /* assume null secret if can't find one */
383 syslog(LOG_WARNING, "No CHAP secret found for authenticating us to %s",
387 /* cancel response send timeout if necessary */
388 if (cstate->clientstate == CHAPCS_RESPONSE)
389 UNTIMEOUT(ChapResponseTimeout, (caddr_t) cstate);
391 cstate->resp_id = id;
392 cstate->resp_transmits = 0;
394 /* generate MD based on negotiated type */
395 switch (cstate->resp_type) {
397 case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
399 MD5Update(&mdContext, &cstate->resp_id, 1);
400 MD5Update(&mdContext, secret, secret_len);
401 MD5Update(&mdContext, rchallenge, rchallenge_len);
402 MD5Final(&mdContext);
403 BCOPY(mdContext.digest, cstate->response, MD5_SIGNATURE_SIZE);
404 cstate->resp_length = MD5_SIGNATURE_SIZE;
408 CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->resp_type));
412 ChapSendResponse(cstate);
417 * ChapReceiveResponse - Receive and process response.
420 ChapReceiveResponse(cstate, inp, id, len)
426 u_char *remmd, remmd_len;
427 int secret_len, old_state;
433 char secret[MAXSECRETLEN];
435 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id));
437 if (cstate->serverstate == CHAPSS_CLOSED ||
438 cstate->serverstate == CHAPSS_PENDING) {
439 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d",
440 cstate->serverstate));
444 if (id != cstate->chal_id)
445 return; /* doesn't match ID of last challenge */
448 * If we have received a duplicate or bogus Response,
449 * we have to send the same answer (Success/Failure)
450 * as we did for the first Response we saw.
452 if (cstate->serverstate == CHAPSS_OPEN) {
453 ChapSendStatus(cstate, CHAP_SUCCESS);
456 if (cstate->serverstate == CHAPSS_BADAUTH) {
457 ChapSendStatus(cstate, CHAP_FAILURE);
462 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
465 GETCHAR(remmd_len, inp); /* get length of MD */
466 remmd = inp; /* get pointer to MD */
467 INCPTR(remmd_len, inp);
469 len -= sizeof (u_char) + remmd_len;
471 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
475 UNTIMEOUT(ChapChallengeTimeout, (caddr_t) cstate);
477 if (len >= sizeof(rhostname))
478 len = sizeof(rhostname) - 1;
479 BCOPY(inp, rhostname, len);
480 rhostname[len] = '\000';
482 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s",
486 * Get secret for authenticating them with us,
487 * do the hash ourselves, and compare the result.
490 if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
491 secret, &secret_len, 1)) {
492 syslog(LOG_WARNING, "No CHAP secret found for authenticating %s",
496 /* generate MD based on negotiated type */
497 switch (cstate->chal_type) {
499 case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
500 if (remmd_len != MD5_SIGNATURE_SIZE)
501 break; /* it's not even the right length */
503 MD5Update(&mdContext, &cstate->chal_id, 1);
504 MD5Update(&mdContext, secret, secret_len);
505 MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
506 MD5Final(&mdContext);
508 /* compare local and remote MDs and send the appropriate status */
509 if (bcmp (mdContext.digest, remmd, MD5_SIGNATURE_SIZE) == 0)
510 code = CHAP_SUCCESS; /* they are the same! */
514 CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->chal_type));
518 ChapSendStatus(cstate, code);
520 if (code == CHAP_SUCCESS) {
521 old_state = cstate->serverstate;
522 cstate->serverstate = CHAPSS_OPEN;
523 if (old_state == CHAPSS_INITIAL_CHAL) {
524 auth_peer_success(cstate->unit, CHAP);
526 if (cstate->chal_interval != 0)
527 TIMEOUT(ChapRechallenge, (caddr_t) cstate, cstate->chal_interval);
530 syslog(LOG_ERR, "CHAP peer authentication failed");
531 cstate->serverstate = CHAPSS_BADAUTH;
532 auth_peer_fail(cstate->unit, CHAP);
537 * ChapReceiveSuccess - Receive Success
540 ChapReceiveSuccess(cstate, inp, id, len)
547 CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id));
549 if (cstate->clientstate == CHAPCS_OPEN)
550 /* presumably an answer to a duplicate response */
553 if (cstate->clientstate != CHAPCS_RESPONSE) {
554 /* don't know what this is */
555 CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n",
556 cstate->clientstate));
566 cstate->clientstate = CHAPCS_OPEN;
568 auth_withpeer_success(cstate->unit, CHAP);
573 * ChapReceiveFailure - Receive failure.
576 ChapReceiveFailure(cstate, inp, id, len)
585 CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id));
587 if (cstate->clientstate != CHAPCS_RESPONSE) {
588 /* don't know what this is */
589 CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n",
590 cstate->clientstate));
600 syslog(LOG_ERR, "CHAP authentication failed");
601 auth_withpeer_fail(cstate->unit, CHAP);
606 * ChapSendChallenge - Send an Authenticate challenge.
609 ChapSendChallenge(cstate)
613 int chal_len, name_len;
616 chal_len = cstate->chal_len;
617 name_len = strlen(cstate->chal_name);
618 outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
619 outp = outpacket_buf;
621 MAKEHEADER(outp, CHAP); /* paste in a CHAP header */
623 PUTCHAR(CHAP_CHALLENGE, outp);
624 PUTCHAR(cstate->chal_id, outp);
625 PUTSHORT(outlen, outp);
627 PUTCHAR(chal_len, outp); /* put length of challenge */
628 BCOPY(cstate->challenge, outp, chal_len);
629 INCPTR(chal_len, outp);
631 BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
633 output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN);
635 CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->chal_id));
637 TIMEOUT(ChapChallengeTimeout, (caddr_t) cstate, cstate->timeouttime);
638 ++cstate->chal_transmits;
643 * ChapSendStatus - Send a status response (ack or nak).
646 ChapSendStatus(cstate, code)
654 if (code == CHAP_SUCCESS)
655 sprintf(msg, "Welcome to %s.", hostname);
657 sprintf(msg, "I don't like you. Go 'way.");
658 msglen = strlen(msg);
660 outlen = CHAP_HEADERLEN + msglen;
661 outp = outpacket_buf;
663 MAKEHEADER(outp, CHAP); /* paste in a header */
666 PUTCHAR(cstate->chal_id, outp);
667 PUTSHORT(outlen, outp);
668 BCOPY(msg, outp, msglen);
669 output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN);
671 CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code,
676 * ChapGenChallenge is used to generate a pseudo-random challenge string of
677 * a pseudo-random length between min_len and max_len. The challenge
678 * string and its length are stored in *cstate, and various other fields of
679 * *cstate are initialized.
683 ChapGenChallenge(cstate)
687 u_char *ptr = cstate->challenge;
690 /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
691 MAX_CHALLENGE_LENGTH */
692 chal_len = (unsigned) ((drand48() *
693 (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
694 MIN_CHALLENGE_LENGTH);
695 cstate->chal_len = chal_len;
696 cstate->chal_id = ++cstate->id;
697 cstate->chal_transmits = 0;
699 /* generate a random string */
700 for (i = 0; i < chal_len; i++ )
701 *ptr++ = (char) (drand48() * 0xff);
705 * ChapSendResponse - send a response packet with values as specified
710 ChapSendResponse(cstate)
714 int outlen, md_len, name_len;
716 md_len = cstate->resp_length;
717 name_len = strlen(cstate->resp_name);
718 outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
719 outp = outpacket_buf;
721 MAKEHEADER(outp, CHAP);
723 PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
724 PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
725 PUTSHORT(outlen, outp); /* packet length */
727 PUTCHAR(md_len, outp); /* length of MD */
728 BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
729 INCPTR(md_len, outp);
731 BCOPY(cstate->resp_name, outp, name_len); /* append our name */
733 /* send the packet */
734 output(cstate->unit, outpacket_buf, outlen + DLLHEADERLEN);
736 cstate->clientstate = CHAPCS_RESPONSE;
737 TIMEOUT(ChapResponseTimeout, (caddr_t) cstate, cstate->timeouttime);
738 ++cstate->resp_transmits;
745 return (double)random() / (double)0x7fffffffL; /* 2**31-1 */
748 void srand48(seedval)