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.
36 #define RCSID "$Id: chap.c,v 1.5 2004/10/06 10:21:52 honor Exp $"
45 #include <sys/types.h>
55 static const char rcsid
[] = RCSID
;
58 * Command-line options.
60 static option_t chap_option_list
[] = {
61 { "chap-restart", o_int
, &chap
[0].timeouttime
,
62 "Set timeout for CHAP", OPT_PRIO
},
63 { "chap-max-challenge", o_int
, &chap
[0].max_transmits
,
64 "Set max #xmits for challenge", OPT_PRIO
},
65 { "chap-interval", o_int
, &chap
[0].chal_interval
,
66 "Set interval for rechallenge", OPT_PRIO
},
68 { "ms-lanman", o_bool
, &ms_lanman
,
69 "Use LanMan passwd when using MS-CHAP", 1 },
75 * Protocol entry points.
77 static void ChapInit
__P((int));
78 static void ChapLowerUp
__P((int));
79 static void ChapLowerDown
__P((int));
80 static void ChapInput
__P((int, u_char
*, int));
81 static void ChapProtocolReject
__P((int));
82 static int ChapPrintPkt
__P((u_char
*, int,
83 void (*) __P((void *, char *, ...)), void *));
85 struct protent chap_protent
= {
105 chap_state chap
[NUM_PPP
]; /* CHAP state; one for each unit */
107 static void ChapChallengeTimeout
__P((void *));
108 static void ChapResponseTimeout
__P((void *));
109 static void ChapReceiveChallenge
__P((chap_state
*, u_char
*, int, int));
110 static void ChapRechallenge
__P((void *));
111 static void ChapReceiveResponse
__P((chap_state
*, u_char
*, int, int));
112 static void ChapReceiveSuccess
__P((chap_state
*, u_char
*, int, int));
113 static void ChapReceiveFailure
__P((chap_state
*, u_char
*, int, int));
114 static void ChapSendStatus
__P((chap_state
*, int));
115 static void ChapSendChallenge
__P((chap_state
*));
116 static void ChapSendResponse
__P((chap_state
*));
117 static void ChapGenChallenge
__P((chap_state
*));
119 extern double drand48
__P((void));
120 extern void srand48
__P((long));
123 * ChapInit - Initialize a CHAP unit.
129 chap_state
*cstate
= &chap
[unit
];
131 BZERO(cstate
, sizeof(*cstate
));
133 cstate
->clientstate
= CHAPCS_INITIAL
;
134 cstate
->serverstate
= CHAPSS_INITIAL
;
135 cstate
->timeouttime
= CHAP_DEFTIMEOUT
;
136 cstate
->max_transmits
= CHAP_DEFTRANSMITS
;
137 /* random number generator is initialized in magic_init */
142 * ChapAuthWithPeer - Authenticate us with our peer (start client).
146 ChapAuthWithPeer(unit
, our_name
, digest
)
151 chap_state
*cstate
= &chap
[unit
];
153 cstate
->resp_name
= our_name
;
154 cstate
->resp_type
= digest
;
156 if (cstate
->clientstate
== CHAPCS_INITIAL
||
157 cstate
->clientstate
== CHAPCS_PENDING
) {
158 /* lower layer isn't up - wait until later */
159 cstate
->clientstate
= CHAPCS_PENDING
;
164 * We get here as a result of LCP coming up.
165 * So even if CHAP was open before, we will
166 * have to re-authenticate ourselves.
168 cstate
->clientstate
= CHAPCS_LISTEN
;
173 * ChapAuthPeer - Authenticate our peer (start server).
176 ChapAuthPeer(unit
, our_name
, digest
)
181 chap_state
*cstate
= &chap
[unit
];
183 cstate
->chal_name
= our_name
;
184 cstate
->chal_type
= digest
;
186 if (cstate
->serverstate
== CHAPSS_INITIAL
||
187 cstate
->serverstate
== CHAPSS_PENDING
) {
188 /* lower layer isn't up - wait until later */
189 cstate
->serverstate
= CHAPSS_PENDING
;
193 ChapGenChallenge(cstate
);
194 ChapSendChallenge(cstate
); /* crank it up dude! */
195 cstate
->serverstate
= CHAPSS_INITIAL_CHAL
;
200 * ChapChallengeTimeout - Timeout expired on sending challenge.
203 ChapChallengeTimeout(arg
)
206 chap_state
*cstate
= (chap_state
*) arg
;
208 /* if we aren't sending challenges, don't worry. then again we */
209 /* probably shouldn't be here either */
210 if (cstate
->serverstate
!= CHAPSS_INITIAL_CHAL
&&
211 cstate
->serverstate
!= CHAPSS_RECHALLENGE
)
214 if (cstate
->chal_transmits
>= cstate
->max_transmits
) {
215 /* give up on peer */
217 LOGX_ERROR("Peer failed to respond to CHAP challenge.");
218 error("Peer failed to respond to CHAP challenge");
220 cstate
->serverstate
= CHAPSS_BADAUTH
;
221 auth_peer_fail(cstate
->unit
, PPP_CHAP
);
225 ChapSendChallenge(cstate
); /* Re-send challenge */
230 * ChapResponseTimeout - Timeout expired on sending response.
233 ChapResponseTimeout(arg
)
236 chap_state
*cstate
= (chap_state
*) arg
;
238 /* if we aren't sending a response, don't worry. */
239 if (cstate
->clientstate
!= CHAPCS_RESPONSE
)
242 ChapSendResponse(cstate
); /* re-send response */
247 * ChapRechallenge - Time to challenge the peer again.
253 chap_state
*cstate
= (chap_state
*) arg
;
255 /* if we aren't sending a response, don't worry. */
256 if (cstate
->serverstate
!= CHAPSS_OPEN
)
259 ChapGenChallenge(cstate
);
260 ChapSendChallenge(cstate
);
261 cstate
->serverstate
= CHAPSS_RECHALLENGE
;
266 * ChapLowerUp - The lower layer is up.
268 * Start up if we have pending requests.
274 chap_state
*cstate
= &chap
[unit
];
276 if (cstate
->clientstate
== CHAPCS_INITIAL
)
277 cstate
->clientstate
= CHAPCS_CLOSED
;
278 else if (cstate
->clientstate
== CHAPCS_PENDING
)
279 cstate
->clientstate
= CHAPCS_LISTEN
;
281 if (cstate
->serverstate
== CHAPSS_INITIAL
)
282 cstate
->serverstate
= CHAPSS_CLOSED
;
283 else if (cstate
->serverstate
== CHAPSS_PENDING
) {
284 ChapGenChallenge(cstate
);
285 ChapSendChallenge(cstate
);
286 cstate
->serverstate
= CHAPSS_INITIAL_CHAL
;
292 * ChapLowerDown - The lower layer is down.
294 * Cancel all timeouts.
300 chap_state
*cstate
= &chap
[unit
];
302 /* Timeout(s) pending? Cancel if so. */
303 if (cstate
->serverstate
== CHAPSS_INITIAL_CHAL
||
304 cstate
->serverstate
== CHAPSS_RECHALLENGE
)
305 UNTIMEOUT(ChapChallengeTimeout
, cstate
);
306 else if (cstate
->serverstate
== CHAPSS_OPEN
307 && cstate
->chal_interval
!= 0)
308 UNTIMEOUT(ChapRechallenge
, cstate
);
309 if (cstate
->clientstate
== CHAPCS_RESPONSE
)
310 UNTIMEOUT(ChapResponseTimeout
, cstate
);
312 cstate
->clientstate
= CHAPCS_INITIAL
;
313 cstate
->serverstate
= CHAPSS_INITIAL
;
318 * ChapProtocolReject - Peer doesn't grok CHAP.
321 ChapProtocolReject(unit
)
324 chap_state
*cstate
= &chap
[unit
];
326 if (cstate
->serverstate
!= CHAPSS_INITIAL
&&
327 cstate
->serverstate
!= CHAPSS_CLOSED
)
328 auth_peer_fail(unit
, PPP_CHAP
);
329 if (cstate
->clientstate
!= CHAPCS_INITIAL
&&
330 cstate
->clientstate
!= CHAPCS_CLOSED
)
331 auth_withpeer_fail(unit
, PPP_CHAP
);
332 ChapLowerDown(unit
); /* shutdown chap */
337 * ChapInput - Input CHAP packet.
340 ChapInput(unit
, inpacket
, packet_len
)
345 chap_state
*cstate
= &chap
[unit
];
351 * Parse header (code, id and length).
352 * If packet too short, drop it.
355 if (packet_len
< CHAP_HEADERLEN
) {
356 CHAPDEBUG(("ChapInput: rcvd short header."));
362 if (len
< CHAP_HEADERLEN
) {
363 CHAPDEBUG(("ChapInput: rcvd illegal length."));
366 if (len
> packet_len
) {
367 CHAPDEBUG(("ChapInput: rcvd short packet."));
370 len
-= CHAP_HEADERLEN
;
373 * Action depends on code (as in fact it usually does :-).
377 ChapReceiveChallenge(cstate
, inp
, id
, len
);
381 ChapReceiveResponse(cstate
, inp
, id
, len
);
385 ChapReceiveFailure(cstate
, inp
, id
, len
);
389 ChapReceiveSuccess(cstate
, inp
, id
, len
);
392 default: /* Need code reject? */
393 warn("Unknown CHAP code (%d) received.", code
);
400 * ChapReceiveChallenge - Receive Challenge and send Response.
403 ChapReceiveChallenge(cstate
, inp
, id
, len
)
412 char secret
[MAXSECRETLEN
];
415 u_char hash
[MD5_SIGNATURE_SIZE
];
417 if (cstate
->clientstate
== CHAPCS_CLOSED
||
418 cstate
->clientstate
== CHAPCS_PENDING
) {
419 CHAPDEBUG(("ChapReceiveChallenge: in state %d", cstate
->clientstate
));
424 CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
428 GETCHAR(rchallenge_len
, inp
);
429 len
-= sizeof (u_char
) + rchallenge_len
; /* now name field length */
431 CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
435 INCPTR(rchallenge_len
, inp
);
437 if (len
>= sizeof(rhostname
))
438 len
= sizeof(rhostname
) - 1;
439 BCOPY(inp
, rhostname
, len
);
440 rhostname
[len
] = '\000';
442 /* Microsoft doesn't send their name back in the PPP packet */
443 if (explicit_remote
|| (remote_name
[0] != 0 && rhostname
[0] == 0)) {
444 strlcpy(rhostname
, remote_name
, sizeof(rhostname
));
445 CHAPDEBUG(("ChapReceiveChallenge: using '%q' as remote name",
449 /* get secret for authenticating ourselves with the specified host */
450 if (!get_secret(cstate
->unit
, cstate
->resp_name
, rhostname
,
451 secret
, &secret_len
, 0)) {
452 secret_len
= 0; /* assume null secret if can't find one */
453 warn("No CHAP secret found for authenticating us to %q", rhostname
);
456 /* cancel response send timeout if necessary */
457 if (cstate
->clientstate
== CHAPCS_RESPONSE
)
458 UNTIMEOUT(ChapResponseTimeout
, cstate
);
460 cstate
->resp_id
= id
;
461 cstate
->resp_transmits
= 0;
463 /* generate MD based on negotiated type */
464 switch (cstate
->resp_type
) {
466 case CHAP_DIGEST_MD5
:
468 MD5Update(&mdContext
, &cstate
->resp_id
, 1);
469 MD5Update(&mdContext
, secret
, secret_len
);
470 MD5Update(&mdContext
, rchallenge
, rchallenge_len
);
471 MD5Final(hash
, &mdContext
);
472 BCOPY(hash
, cstate
->response
, MD5_SIGNATURE_SIZE
);
473 cstate
->resp_length
= MD5_SIGNATURE_SIZE
;
478 ChapMS(cstate
, rchallenge
, rchallenge_len
, secret
, secret_len
);
483 CHAPDEBUG(("unknown digest type %d", cstate
->resp_type
));
487 BZERO(secret
, sizeof(secret
));
488 ChapSendResponse(cstate
);
493 * ChapReceiveResponse - Receive and process response.
496 ChapReceiveResponse(cstate
, inp
, id
, len
)
502 u_char
*remmd
, remmd_len
;
503 int secret_len
, old_state
;
507 char secret
[MAXSECRETLEN
];
508 u_char hash
[MD5_SIGNATURE_SIZE
];
510 if (cstate
->serverstate
== CHAPSS_CLOSED
||
511 cstate
->serverstate
== CHAPSS_PENDING
) {
512 CHAPDEBUG(("ChapReceiveResponse: in state %d", cstate
->serverstate
));
516 if (id
!= cstate
->chal_id
)
517 return; /* doesn't match ID of last challenge */
519 LOGX_INFO("Received CHAP Response.");
522 * If we have received a duplicate or bogus Response,
523 * we have to send the same answer (Success/Failure)
524 * as we did for the first Response we saw.
526 if (cstate
->serverstate
== CHAPSS_OPEN
) {
527 ChapSendStatus(cstate
, CHAP_SUCCESS
);
530 if (cstate
->serverstate
== CHAPSS_BADAUTH
) {
531 ChapSendStatus(cstate
, CHAP_FAILURE
);
536 CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
539 GETCHAR(remmd_len
, inp
); /* get length of MD */
540 remmd
= inp
; /* get pointer to MD */
541 INCPTR(remmd_len
, inp
);
543 len
-= sizeof (u_char
) + remmd_len
;
545 CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
549 UNTIMEOUT(ChapChallengeTimeout
, cstate
);
551 if (len
>= sizeof(rhostname
))
552 len
= sizeof(rhostname
) - 1;
553 BCOPY(inp
, rhostname
, len
);
554 rhostname
[len
] = '\000';
557 * Get secret for authenticating them with us,
558 * do the hash ourselves, and compare the result.
561 if (!get_secret(cstate
->unit
, (explicit_remote
? remote_name
: rhostname
),
562 cstate
->chal_name
, secret
, &secret_len
, 1)) {
563 warn("No CHAP secret found for authenticating %q", rhostname
);
566 /* generate MD based on negotiated type */
567 switch (cstate
->chal_type
) {
569 case CHAP_DIGEST_MD5
: /* only MD5 is defined for now */
570 if (remmd_len
!= MD5_SIGNATURE_SIZE
)
571 break; /* it's not even the right length */
573 MD5Update(&mdContext
, &cstate
->chal_id
, 1);
574 MD5Update(&mdContext
, secret
, secret_len
);
575 MD5Update(&mdContext
, cstate
->challenge
, cstate
->chal_len
);
576 MD5Final(hash
, &mdContext
);
578 /* compare local and remote MDs and send the appropriate status */
579 if (memcmp (hash
, remmd
, MD5_SIGNATURE_SIZE
) == 0)
580 code
= CHAP_SUCCESS
; /* they are the same! */
584 CHAPDEBUG(("unknown digest type %d", cstate
->chal_type
));
588 BZERO(secret
, sizeof(secret
));
589 ChapSendStatus(cstate
, code
);
591 if (code
== CHAP_SUCCESS
) {
592 old_state
= cstate
->serverstate
;
593 cstate
->serverstate
= CHAPSS_OPEN
;
594 if (old_state
== CHAPSS_INITIAL_CHAL
) {
595 auth_peer_success(cstate
->unit
, PPP_CHAP
, rhostname
, len
);
597 if (cstate
->chal_interval
!= 0)
598 TIMEOUT(ChapRechallenge
, cstate
, cstate
->chal_interval
);
600 LOGX_INFO("CHAP peer authentication succeeded for %s.", rhostname
);
601 notice("CHAP peer authentication succeeded for %q", rhostname
);
604 LOGX_WARN("CHAP peer authentication failed for remote host %s.", rhostname
);
605 error("CHAP peer authentication failed for remote host %q", rhostname
);
606 cstate
->serverstate
= CHAPSS_BADAUTH
;
607 auth_peer_fail(cstate
->unit
, PPP_CHAP
);
612 * ChapReceiveSuccess - Receive Success
615 ChapReceiveSuccess(cstate
, inp
, id
, len
)
622 if (cstate
->clientstate
== CHAPCS_OPEN
)
623 /* presumably an answer to a duplicate response */
626 if (cstate
->clientstate
!= CHAPCS_RESPONSE
) {
627 /* don't know what this is */
628 CHAPDEBUG(("ChapReceiveSuccess: in state %d\n", cstate
->clientstate
));
632 LOGX_INFO("CHAP authentication succeeded.");
633 UNTIMEOUT(ChapResponseTimeout
, cstate
);
641 cstate
->clientstate
= CHAPCS_OPEN
;
643 auth_withpeer_success(cstate
->unit
, PPP_CHAP
);
648 * ChapReceiveFailure - Receive failure.
651 ChapReceiveFailure(cstate
, inp
, id
, len
)
657 if (cstate
->clientstate
!= CHAPCS_RESPONSE
) {
658 /* don't know what this is */
659 CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate
->clientstate
));
663 UNTIMEOUT(ChapResponseTimeout
, cstate
);
671 LOGX_ERROR("CHAP authentication failed.");
672 error("CHAP authentication failed");
674 //log_to_file("CHAP_AUTH_FAIL");
675 system("ppp_event -t CHAP_AUTH_FAIL &");
677 auth_withpeer_fail(cstate
->unit
, PPP_CHAP
);
682 * ChapSendChallenge - Send an Authenticate challenge.
685 ChapSendChallenge(cstate
)
689 int chal_len
, name_len
;
692 LOGX_INFO("Sending CHAP challenge.");
694 chal_len
= cstate
->chal_len
;
695 name_len
= strlen(cstate
->chal_name
);
696 outlen
= CHAP_HEADERLEN
+ sizeof (u_char
) + chal_len
+ name_len
;
697 outp
= outpacket_buf
;
699 MAKEHEADER(outp
, PPP_CHAP
); /* paste in a CHAP header */
701 PUTCHAR(CHAP_CHALLENGE
, outp
);
702 PUTCHAR(cstate
->chal_id
, outp
);
703 PUTSHORT(outlen
, outp
);
705 PUTCHAR(chal_len
, outp
); /* put length of challenge */
706 BCOPY(cstate
->challenge
, outp
, chal_len
);
707 INCPTR(chal_len
, outp
);
709 BCOPY(cstate
->chal_name
, outp
, name_len
); /* append hostname */
711 output(cstate
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);
713 TIMEOUT(ChapChallengeTimeout
, cstate
, cstate
->timeouttime
);
714 ++cstate
->chal_transmits
;
719 * ChapSendStatus - Send a status response (ack or nak).
722 ChapSendStatus(cstate
, code
)
730 LOGX_INFO("Sending CHAP %s.", (code
== CHAP_SUCCESS
? "Success" : "Failure"));
732 if (code
== CHAP_SUCCESS
)
733 slprintf(msg
, sizeof(msg
), "Welcome to %s.", hostname
);
735 slprintf(msg
, sizeof(msg
), "I don't like you. Go 'way.");
736 msglen
= strlen(msg
);
738 outlen
= CHAP_HEADERLEN
+ msglen
;
739 outp
= outpacket_buf
;
741 MAKEHEADER(outp
, PPP_CHAP
); /* paste in a header */
744 PUTCHAR(cstate
->chal_id
, outp
);
745 PUTSHORT(outlen
, outp
);
746 BCOPY(msg
, outp
, msglen
);
747 output(cstate
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);
751 * ChapGenChallenge is used to generate a pseudo-random challenge string of
752 * a pseudo-random length between min_len and max_len. The challenge
753 * string and its length are stored in *cstate, and various other fields of
754 * *cstate are initialized.
758 ChapGenChallenge(cstate
)
762 u_char
*ptr
= cstate
->challenge
;
765 /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
766 MAX_CHALLENGE_LENGTH */
767 chal_len
= (unsigned) ((drand48() *
768 (MAX_CHALLENGE_LENGTH
- MIN_CHALLENGE_LENGTH
)) +
769 MIN_CHALLENGE_LENGTH
);
770 cstate
->chal_len
= chal_len
;
771 cstate
->chal_id
= ++cstate
->id
;
772 cstate
->chal_transmits
= 0;
774 /* generate a random string */
775 for (i
= 0; i
< chal_len
; i
++)
776 *ptr
++ = (char) (drand48() * 0xff);
780 * ChapSendResponse - send a response packet with values as specified
785 ChapSendResponse(cstate
)
789 int outlen
, md_len
, name_len
;
791 md_len
= cstate
->resp_length
;
792 name_len
= strlen(cstate
->resp_name
);
793 outlen
= CHAP_HEADERLEN
+ sizeof (u_char
) + md_len
+ name_len
;
794 outp
= outpacket_buf
;
796 MAKEHEADER(outp
, PPP_CHAP
);
798 PUTCHAR(CHAP_RESPONSE
, outp
); /* we are a response */
799 PUTCHAR(cstate
->resp_id
, outp
); /* copy id from challenge packet */
800 PUTSHORT(outlen
, outp
); /* packet length */
802 PUTCHAR(md_len
, outp
); /* length of MD */
803 BCOPY(cstate
->response
, outp
, md_len
); /* copy MD to buffer */
804 INCPTR(md_len
, outp
);
806 BCOPY(cstate
->resp_name
, outp
, name_len
); /* append our name */
808 /* send the packet */
809 output(cstate
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);
811 cstate
->clientstate
= CHAPCS_RESPONSE
;
812 TIMEOUT(ChapResponseTimeout
, cstate
, cstate
->timeouttime
);
813 ++cstate
->resp_transmits
;
817 * ChapPrintPkt - print the contents of a CHAP packet.
819 static char *ChapCodenames
[] = {
820 "Challenge", "Response", "Success", "Failure"
824 ChapPrintPkt(p
, plen
, printer
, arg
)
827 void (*printer
) __P((void *, char *, ...));
834 if (plen
< CHAP_HEADERLEN
)
839 if (len
< CHAP_HEADERLEN
|| len
> plen
)
842 if (code
>= 1 && code
<= sizeof(ChapCodenames
) / sizeof(char *))
843 printer(arg
, " %s", ChapCodenames
[code
-1]);
845 printer(arg
, " code=0x%x", code
);
846 printer(arg
, " id=0x%x", id
);
847 len
-= CHAP_HEADERLEN
;
857 nlen
= len
- clen
- 1;
859 for (; clen
> 0; --clen
) {
861 printer(arg
, "%.2x", x
);
863 printer(arg
, ">, name = ");
864 print_string((char *)p
, nlen
, printer
, arg
);
869 print_string((char *)p
, len
, printer
, arg
);
872 for (clen
= len
; clen
> 0; --clen
) {
874 printer(arg
, " %.2x", x
);
878 return len
+ CHAP_HEADERLEN
;