2 * chap.c - Challenge Handshake Authentication Protocol.
4 * Copyright (c) 1993 The Australian National University.
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 the Australian National University. The name of the University
13 * may not be used to endorse or promote products derived from this
14 * software without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 * Copyright (c) 1991 Gregory M. Christy.
20 * All rights reserved.
22 * Redistribution and use in source and binary forms are permitted
23 * provided that the above copyright notice and this paragraph are
24 * duplicated in all such forms and that any documentation,
25 * advertising materials, and other materials related to such
26 * distribution and use acknowledge that the software was developed
27 * by Gregory M. Christy. The name of the author may not be used to
28 * endorse or promote products derived from this software without
29 * specific prior written permission.
31 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
32 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
33 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
37 static char rcsid
[] = "$FreeBSD$";
46 #include <sys/types.h>
59 * Protocol entry points.
61 static void ChapInit(int);
62 static void ChapLowerUp(int);
63 static void ChapLowerDown(int);
64 static void ChapInput(int, u_char
*, int);
65 static void ChapProtocolReject(int);
66 static int ChapPrintPkt(u_char
*, int,
67 void (*)(void *, char *, ...), void *);
69 struct protent chap_protent
= {
87 chap_state chap
[NUM_PPP
]; /* CHAP state; one for each unit */
89 static void ChapChallengeTimeout(void *);
90 static void ChapResponseTimeout(void *);
91 static void ChapReceiveChallenge(chap_state
*, u_char
*, int, int);
92 static void ChapRechallenge(void *);
93 static void ChapReceiveResponse(chap_state
*, u_char
*, int, int);
94 static void ChapReceiveSuccess(chap_state
*, u_char
*, int, int);
95 static void ChapReceiveFailure(chap_state
*, u_char
*, int, int);
96 static void ChapSendStatus(chap_state
*, int);
97 static void ChapSendChallenge(chap_state
*);
98 static void ChapSendResponse(chap_state
*);
99 static void ChapGenChallenge(chap_state
*);
101 extern double drand48(void);
102 extern void srand48(long);
105 * ChapInit - Initialize a CHAP unit.
111 chap_state
*cstate
= &chap
[unit
];
113 BZERO(cstate
, sizeof(*cstate
));
115 cstate
->clientstate
= CHAPCS_INITIAL
;
116 cstate
->serverstate
= CHAPSS_INITIAL
;
117 cstate
->timeouttime
= CHAP_DEFTIMEOUT
;
118 cstate
->max_transmits
= CHAP_DEFTRANSMITS
;
119 /* random number generator is initialized in magic_init */
124 * ChapAuthWithPeer - Authenticate us with our peer (start client).
128 ChapAuthWithPeer(unit
, our_name
, digest
)
133 chap_state
*cstate
= &chap
[unit
];
135 cstate
->resp_name
= our_name
;
136 cstate
->resp_type
= digest
;
138 if (cstate
->clientstate
== CHAPCS_INITIAL
||
139 cstate
->clientstate
== CHAPCS_PENDING
) {
140 /* lower layer isn't up - wait until later */
141 cstate
->clientstate
= CHAPCS_PENDING
;
146 * We get here as a result of LCP coming up.
147 * So even if CHAP was open before, we will
148 * have to re-authenticate ourselves.
150 cstate
->clientstate
= CHAPCS_LISTEN
;
155 * ChapAuthPeer - Authenticate our peer (start server).
158 ChapAuthPeer(unit
, our_name
, digest
)
163 chap_state
*cstate
= &chap
[unit
];
165 cstate
->chal_name
= our_name
;
166 cstate
->chal_type
= digest
;
168 if (cstate
->serverstate
== CHAPSS_INITIAL
||
169 cstate
->serverstate
== CHAPSS_PENDING
) {
170 /* lower layer isn't up - wait until later */
171 cstate
->serverstate
= CHAPSS_PENDING
;
175 ChapGenChallenge(cstate
);
176 ChapSendChallenge(cstate
); /* crank it up dude! */
177 cstate
->serverstate
= CHAPSS_INITIAL_CHAL
;
182 * ChapChallengeTimeout - Timeout expired on sending challenge.
185 ChapChallengeTimeout(arg
)
188 chap_state
*cstate
= (chap_state
*) arg
;
190 /* if we aren't sending challenges, don't worry. then again we */
191 /* probably shouldn't be here either */
192 if (cstate
->serverstate
!= CHAPSS_INITIAL_CHAL
&&
193 cstate
->serverstate
!= CHAPSS_RECHALLENGE
)
196 if (cstate
->chal_transmits
>= cstate
->max_transmits
) {
197 /* give up on peer */
198 syslog(LOG_ERR
, "Peer failed to respond to CHAP challenge");
199 cstate
->serverstate
= CHAPSS_BADAUTH
;
200 auth_peer_fail(cstate
->unit
, PPP_CHAP
);
204 ChapSendChallenge(cstate
); /* Re-send challenge */
209 * ChapResponseTimeout - Timeout expired on sending response.
212 ChapResponseTimeout(arg
)
215 chap_state
*cstate
= (chap_state
*) arg
;
217 /* if we aren't sending a response, don't worry. */
218 if (cstate
->clientstate
!= CHAPCS_RESPONSE
)
221 ChapSendResponse(cstate
); /* re-send response */
226 * ChapRechallenge - Time to challenge the peer again.
232 chap_state
*cstate
= (chap_state
*) arg
;
234 /* if we aren't sending a response, don't worry. */
235 if (cstate
->serverstate
!= CHAPSS_OPEN
)
238 ChapGenChallenge(cstate
);
239 ChapSendChallenge(cstate
);
240 cstate
->serverstate
= CHAPSS_RECHALLENGE
;
245 * ChapLowerUp - The lower layer is up.
247 * Start up if we have pending requests.
253 chap_state
*cstate
= &chap
[unit
];
255 if (cstate
->clientstate
== CHAPCS_INITIAL
)
256 cstate
->clientstate
= CHAPCS_CLOSED
;
257 else if (cstate
->clientstate
== CHAPCS_PENDING
)
258 cstate
->clientstate
= CHAPCS_LISTEN
;
260 if (cstate
->serverstate
== CHAPSS_INITIAL
)
261 cstate
->serverstate
= CHAPSS_CLOSED
;
262 else if (cstate
->serverstate
== CHAPSS_PENDING
) {
263 ChapGenChallenge(cstate
);
264 ChapSendChallenge(cstate
);
265 cstate
->serverstate
= CHAPSS_INITIAL_CHAL
;
271 * ChapLowerDown - The lower layer is down.
273 * Cancel all timeouts.
279 chap_state
*cstate
= &chap
[unit
];
281 /* Timeout(s) pending? Cancel if so. */
282 if (cstate
->serverstate
== CHAPSS_INITIAL_CHAL
||
283 cstate
->serverstate
== CHAPSS_RECHALLENGE
)
284 UNTIMEOUT(ChapChallengeTimeout
, cstate
);
285 else if (cstate
->serverstate
== CHAPSS_OPEN
286 && cstate
->chal_interval
!= 0)
287 UNTIMEOUT(ChapRechallenge
, cstate
);
288 if (cstate
->clientstate
== CHAPCS_RESPONSE
)
289 UNTIMEOUT(ChapResponseTimeout
, cstate
);
291 cstate
->clientstate
= CHAPCS_INITIAL
;
292 cstate
->serverstate
= CHAPSS_INITIAL
;
297 * ChapProtocolReject - Peer doesn't grok CHAP.
300 ChapProtocolReject(unit
)
303 chap_state
*cstate
= &chap
[unit
];
305 if (cstate
->serverstate
!= CHAPSS_INITIAL
&&
306 cstate
->serverstate
!= CHAPSS_CLOSED
)
307 auth_peer_fail(unit
, PPP_CHAP
);
308 if (cstate
->clientstate
!= CHAPCS_INITIAL
&&
309 cstate
->clientstate
!= CHAPCS_CLOSED
)
310 auth_withpeer_fail(unit
, PPP_CHAP
);
311 ChapLowerDown(unit
); /* shutdown chap */
316 * ChapInput - Input CHAP packet.
319 ChapInput(unit
, inpacket
, packet_len
)
324 chap_state
*cstate
= &chap
[unit
];
330 * Parse header (code, id and length).
331 * If packet too short, drop it.
334 if (packet_len
< CHAP_HEADERLEN
) {
335 CHAPDEBUG((LOG_INFO
, "ChapInput: rcvd short header."));
341 if (len
< CHAP_HEADERLEN
) {
342 CHAPDEBUG((LOG_INFO
, "ChapInput: rcvd illegal length."));
345 if (len
> packet_len
) {
346 CHAPDEBUG((LOG_INFO
, "ChapInput: rcvd short packet."));
349 len
-= CHAP_HEADERLEN
;
352 * Action depends on code (as in fact it usually does :-).
356 ChapReceiveChallenge(cstate
, inp
, id
, len
);
360 ChapReceiveResponse(cstate
, inp
, id
, len
);
364 ChapReceiveFailure(cstate
, inp
, id
, len
);
368 ChapReceiveSuccess(cstate
, inp
, id
, len
);
371 default: /* Need code reject? */
372 syslog(LOG_WARNING
, "Unknown CHAP code (%d) received.", code
);
379 * ChapReceiveChallenge - Receive Challenge and send Response.
382 ChapReceiveChallenge(cstate
, inp
, id
, len
)
391 char secret
[MAXSECRETLEN
];
394 u_char hash
[MD5_SIGNATURE_SIZE
];
396 CHAPDEBUG((LOG_INFO
, "ChapReceiveChallenge: Rcvd id %d.", id
));
397 if (cstate
->clientstate
== CHAPCS_CLOSED
||
398 cstate
->clientstate
== CHAPCS_PENDING
) {
399 CHAPDEBUG((LOG_INFO
, "ChapReceiveChallenge: in state %d",
400 cstate
->clientstate
));
405 CHAPDEBUG((LOG_INFO
, "ChapReceiveChallenge: rcvd short packet."));
409 GETCHAR(rchallenge_len
, inp
);
410 len
-= sizeof (u_char
) + rchallenge_len
; /* now name field length */
412 CHAPDEBUG((LOG_INFO
, "ChapReceiveChallenge: rcvd short packet."));
416 INCPTR(rchallenge_len
, inp
);
418 if (len
>= sizeof(rhostname
))
419 len
= sizeof(rhostname
) - 1;
420 BCOPY(inp
, rhostname
, len
);
421 rhostname
[len
] = '\000';
423 CHAPDEBUG((LOG_INFO
, "ChapReceiveChallenge: received name field '%s'",
426 /* Microsoft doesn't send their name back in the PPP packet */
427 if (remote_name
[0] != 0 && (explicit_remote
|| rhostname
[0] == 0)) {
428 strncpy(rhostname
, remote_name
, sizeof(rhostname
));
429 rhostname
[sizeof(rhostname
) - 1] = 0;
430 CHAPDEBUG((LOG_INFO
, "ChapReceiveChallenge: using '%s' as remote name",
434 /* get secret for authenticating ourselves with the specified host */
435 if (!get_secret(cstate
->unit
, cstate
->resp_name
, rhostname
,
436 secret
, &secret_len
, 0)) {
437 secret_len
= 0; /* assume null secret if can't find one */
438 syslog(LOG_WARNING
, "No CHAP secret found for authenticating us to %s",
442 /* cancel response send timeout if necessary */
443 if (cstate
->clientstate
== CHAPCS_RESPONSE
)
444 UNTIMEOUT(ChapResponseTimeout
, cstate
);
446 cstate
->resp_id
= id
;
447 cstate
->resp_transmits
= 0;
449 /* generate MD based on negotiated type */
450 switch (cstate
->resp_type
) {
452 case CHAP_DIGEST_MD5
:
454 MD5Update(&mdContext
, &cstate
->resp_id
, 1);
455 MD5Update(&mdContext
, secret
, secret_len
);
456 MD5Update(&mdContext
, rchallenge
, rchallenge_len
);
457 MD5Final(hash
, &mdContext
);
458 BCOPY(hash
, cstate
->response
, MD5_SIGNATURE_SIZE
);
459 cstate
->resp_length
= MD5_SIGNATURE_SIZE
;
464 ChapMS(cstate
, rchallenge
, rchallenge_len
, secret
, secret_len
);
469 CHAPDEBUG((LOG_INFO
, "unknown digest type %d", cstate
->resp_type
));
473 BZERO(secret
, sizeof(secret
));
474 ChapSendResponse(cstate
);
479 * ChapReceiveResponse - Receive and process response.
482 ChapReceiveResponse(cstate
, inp
, id
, len
)
488 u_char
*remmd
, remmd_len
;
489 int secret_len
, old_state
;
493 char secret
[MAXSECRETLEN
];
494 u_char hash
[MD5_SIGNATURE_SIZE
];
496 CHAPDEBUG((LOG_INFO
, "ChapReceiveResponse: Rcvd id %d.", id
));
498 if (cstate
->serverstate
== CHAPSS_CLOSED
||
499 cstate
->serverstate
== CHAPSS_PENDING
) {
500 CHAPDEBUG((LOG_INFO
, "ChapReceiveResponse: in state %d",
501 cstate
->serverstate
));
505 if (id
!= cstate
->chal_id
)
506 return; /* doesn't match ID of last challenge */
509 * If we have received a duplicate or bogus Response,
510 * we have to send the same answer (Success/Failure)
511 * as we did for the first Response we saw.
513 if (cstate
->serverstate
== CHAPSS_OPEN
) {
514 ChapSendStatus(cstate
, CHAP_SUCCESS
);
517 if (cstate
->serverstate
== CHAPSS_BADAUTH
) {
518 ChapSendStatus(cstate
, CHAP_FAILURE
);
523 CHAPDEBUG((LOG_INFO
, "ChapReceiveResponse: rcvd short packet."));
526 GETCHAR(remmd_len
, inp
); /* get length of MD */
527 remmd
= inp
; /* get pointer to MD */
528 INCPTR(remmd_len
, inp
);
530 len
-= sizeof (u_char
) + remmd_len
;
532 CHAPDEBUG((LOG_INFO
, "ChapReceiveResponse: rcvd short packet."));
536 UNTIMEOUT(ChapChallengeTimeout
, cstate
);
538 if (len
>= sizeof(rhostname
))
539 len
= sizeof(rhostname
) - 1;
540 BCOPY(inp
, rhostname
, len
);
541 rhostname
[len
] = '\000';
543 CHAPDEBUG((LOG_INFO
, "ChapReceiveResponse: received name field: %s",
547 * Get secret for authenticating them with us,
548 * do the hash ourselves, and compare the result.
551 if (!get_secret(cstate
->unit
, rhostname
, cstate
->chal_name
,
552 secret
, &secret_len
, 1)) {
553 syslog(LOG_WARNING
, "No CHAP secret found for authenticating %s",
557 /* generate MD based on negotiated type */
558 switch (cstate
->chal_type
) {
560 case CHAP_DIGEST_MD5
: /* only MD5 is defined for now */
561 if (remmd_len
!= MD5_SIGNATURE_SIZE
)
562 break; /* it's not even the right length */
564 MD5Update(&mdContext
, &cstate
->chal_id
, 1);
565 MD5Update(&mdContext
, secret
, secret_len
);
566 MD5Update(&mdContext
, cstate
->challenge
, cstate
->chal_len
);
567 MD5Final(hash
, &mdContext
);
569 /* compare local and remote MDs and send the appropriate status */
570 if (memcmp (hash
, remmd
, MD5_SIGNATURE_SIZE
) == 0)
571 code
= CHAP_SUCCESS
; /* they are the same! */
575 CHAPDEBUG((LOG_INFO
, "unknown digest type %d", cstate
->chal_type
));
579 BZERO(secret
, sizeof(secret
));
580 ChapSendStatus(cstate
, code
);
582 if (code
== CHAP_SUCCESS
) {
583 old_state
= cstate
->serverstate
;
584 cstate
->serverstate
= CHAPSS_OPEN
;
585 if (old_state
== CHAPSS_INITIAL_CHAL
) {
586 auth_peer_success(cstate
->unit
, PPP_CHAP
, rhostname
, len
);
588 if (cstate
->chal_interval
!= 0)
589 TIMEOUT(ChapRechallenge
, cstate
, cstate
->chal_interval
);
590 syslog(LOG_NOTICE
, "CHAP peer authentication succeeded for %s",
594 syslog(LOG_ERR
, "CHAP peer authentication failed for remote host %s",
596 cstate
->serverstate
= CHAPSS_BADAUTH
;
597 auth_peer_fail(cstate
->unit
, PPP_CHAP
);
602 * ChapReceiveSuccess - Receive Success
605 ChapReceiveSuccess(cstate
, inp
, id
, len
)
612 CHAPDEBUG((LOG_INFO
, "ChapReceiveSuccess: Rcvd id %d.", id
));
614 if (cstate
->clientstate
== CHAPCS_OPEN
)
615 /* presumably an answer to a duplicate response */
618 if (cstate
->clientstate
!= CHAPCS_RESPONSE
) {
619 /* don't know what this is */
620 CHAPDEBUG((LOG_INFO
, "ChapReceiveSuccess: in state %d\n",
621 cstate
->clientstate
));
625 UNTIMEOUT(ChapResponseTimeout
, cstate
);
633 cstate
->clientstate
= CHAPCS_OPEN
;
635 auth_withpeer_success(cstate
->unit
, PPP_CHAP
);
640 * ChapReceiveFailure - Receive failure.
643 ChapReceiveFailure(cstate
, inp
, id
, len
)
649 CHAPDEBUG((LOG_INFO
, "ChapReceiveFailure: Rcvd id %d.", id
));
651 if (cstate
->clientstate
!= CHAPCS_RESPONSE
) {
652 /* don't know what this is */
653 CHAPDEBUG((LOG_INFO
, "ChapReceiveFailure: in state %d\n",
654 cstate
->clientstate
));
658 UNTIMEOUT(ChapResponseTimeout
, cstate
);
666 syslog(LOG_ERR
, "CHAP authentication failed");
667 auth_withpeer_fail(cstate
->unit
, PPP_CHAP
);
672 * ChapSendChallenge - Send an Authenticate challenge.
675 ChapSendChallenge(cstate
)
679 int chal_len
, name_len
;
682 chal_len
= cstate
->chal_len
;
683 name_len
= strlen(cstate
->chal_name
);
684 outlen
= CHAP_HEADERLEN
+ sizeof (u_char
) + chal_len
+ name_len
;
685 outp
= outpacket_buf
;
687 MAKEHEADER(outp
, PPP_CHAP
); /* paste in a CHAP header */
689 PUTCHAR(CHAP_CHALLENGE
, outp
);
690 PUTCHAR(cstate
->chal_id
, outp
);
691 PUTSHORT(outlen
, outp
);
693 PUTCHAR(chal_len
, outp
); /* put length of challenge */
694 BCOPY(cstate
->challenge
, outp
, chal_len
);
695 INCPTR(chal_len
, outp
);
697 BCOPY(cstate
->chal_name
, outp
, name_len
); /* append hostname */
699 output(cstate
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);
701 CHAPDEBUG((LOG_INFO
, "ChapSendChallenge: Sent id %d.", cstate
->chal_id
));
703 TIMEOUT(ChapChallengeTimeout
, cstate
, cstate
->timeouttime
);
704 ++cstate
->chal_transmits
;
709 * ChapSendStatus - Send a status response (ack or nak).
712 ChapSendStatus(cstate
, code
)
720 if (code
== CHAP_SUCCESS
)
721 sprintf(msg
, "Welcome to %s.", hostname
);
723 sprintf(msg
, "I don't like you. Go 'way.");
724 msglen
= strlen(msg
);
726 outlen
= CHAP_HEADERLEN
+ msglen
;
727 outp
= outpacket_buf
;
729 MAKEHEADER(outp
, PPP_CHAP
); /* paste in a header */
732 PUTCHAR(cstate
->chal_id
, outp
);
733 PUTSHORT(outlen
, outp
);
734 BCOPY(msg
, outp
, msglen
);
735 output(cstate
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);
737 CHAPDEBUG((LOG_INFO
, "ChapSendStatus: Sent code %d, id %d.", code
,
742 * ChapGenChallenge is used to generate a pseudo-random challenge string of
743 * a pseudo-random length between min_len and max_len. The challenge
744 * string and its length are stored in *cstate, and various other fields of
745 * *cstate are initialized.
749 ChapGenChallenge(cstate
)
753 u_char
*ptr
= cstate
->challenge
;
756 /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
757 MAX_CHALLENGE_LENGTH */
758 chal_len
= (unsigned) ((drand48() *
759 (MAX_CHALLENGE_LENGTH
- MIN_CHALLENGE_LENGTH
)) +
760 MIN_CHALLENGE_LENGTH
);
761 cstate
->chal_len
= chal_len
;
762 cstate
->chal_id
= ++cstate
->id
;
763 cstate
->chal_transmits
= 0;
765 /* generate a random string */
766 for (i
= 0; i
< chal_len
; i
++ )
767 *ptr
++ = (char) (drand48() * 0xff);
771 * ChapSendResponse - send a response packet with values as specified
776 ChapSendResponse(cstate
)
780 int outlen
, md_len
, name_len
;
782 md_len
= cstate
->resp_length
;
783 name_len
= strlen(cstate
->resp_name
);
784 outlen
= CHAP_HEADERLEN
+ sizeof (u_char
) + md_len
+ name_len
;
785 outp
= outpacket_buf
;
787 MAKEHEADER(outp
, PPP_CHAP
);
789 PUTCHAR(CHAP_RESPONSE
, outp
); /* we are a response */
790 PUTCHAR(cstate
->resp_id
, outp
); /* copy id from challenge packet */
791 PUTSHORT(outlen
, outp
); /* packet length */
793 PUTCHAR(md_len
, outp
); /* length of MD */
794 BCOPY(cstate
->response
, outp
, md_len
); /* copy MD to buffer */
795 INCPTR(md_len
, outp
);
797 BCOPY(cstate
->resp_name
, outp
, name_len
); /* append our name */
799 /* send the packet */
800 output(cstate
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);
802 cstate
->clientstate
= CHAPCS_RESPONSE
;
803 TIMEOUT(ChapResponseTimeout
, cstate
, cstate
->timeouttime
);
804 ++cstate
->resp_transmits
;
808 * ChapPrintPkt - print the contents of a CHAP packet.
810 static char *ChapCodenames
[] = {
811 "Challenge", "Response", "Success", "Failure"
815 ChapPrintPkt(p
, plen
, printer
, arg
)
818 void (*printer
)(void *, char *, ...);
825 if (plen
< CHAP_HEADERLEN
)
830 if (len
< CHAP_HEADERLEN
|| len
> plen
)
833 if (code
>= 1 && code
<= sizeof(ChapCodenames
) / sizeof(char *))
834 printer(arg
, " %s", ChapCodenames
[code
-1]);
836 printer(arg
, " code=0x%x", code
);
837 printer(arg
, " id=0x%x", id
);
838 len
-= CHAP_HEADERLEN
;
848 nlen
= len
- clen
- 1;
850 for (; clen
> 0; --clen
) {
852 printer(arg
, "%.2x", x
);
854 printer(arg
, ">, name = ");
855 print_string((char *)p
, nlen
, printer
, arg
);
860 print_string((char *)p
, len
, printer
, arg
);
863 for (clen
= len
; clen
> 0; --clen
) {
865 printer(arg
, " %.2x", x
);
869 return len
+ CHAP_HEADERLEN
;