1/*
2 * chap.c - Challenge Handshake Authentication Protocol.
3 *
4 * Copyright (c) 1993 The Australian National University.
5 * All rights reserved.
6 *
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.
18 *
19 * Copyright (c) 1991 Gregory M. Christy.
20 * All rights reserved.
21 *
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.
30 *
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.
34 */
35
36#define RCSID	"$Id$"
37
38/*
39 * TODO:
40 */
41
42#include <stdio.h>
43#include <string.h>
44#include <sys/types.h>
45#include <sys/time.h>
46
47#include <pppd.h>
48#include "chap.h"
49#include "md5.h"
50#ifdef CHAPMS
51#include "chap_ms.h"
52#endif
53
54static const char rcsid[] = RCSID;
55
56/*
57 * Command-line options.
58 */
59static option_t chap_option_list[] = {
60    { "chap-restart", o_int, &chap[0].timeouttime,
61      "Set timeout for CHAP", OPT_PRIO },
62    { "chap-max-challenge", o_int, &chap[0].max_transmits,
63      "Set max #xmits for challenge", OPT_PRIO },
64    { "chap-interval", o_int, &chap[0].chal_interval,
65      "Set interval for rechallenge", OPT_PRIO },
66#ifdef MSLANMAN
67    { "ms-lanman", o_bool, &ms_lanman,
68      "Use LanMan passwd when using MS-CHAP", 1 },
69#endif
70    { NULL }
71};
72
73/*
74 * Protocol entry points.
75 */
76static void ChapInit __P((int));
77static void ChapLowerUp __P((int));
78static void ChapLowerDown __P((int));
79static void ChapInput __P((int, u_char *, int));
80static void ChapProtocolReject __P((int));
81static int  ChapPrintPkt __P((u_char *, int,
82			      void (*) __P((void *, char *, ...)), void *));
83
84struct protent chap_protent = {
85    PPP_CHAP,
86    ChapInit,
87    ChapInput,
88    ChapProtocolReject,
89    ChapLowerUp,
90    ChapLowerDown,
91    NULL,
92    NULL,
93    ChapPrintPkt,
94    NULL,
95    1,
96    "CHAP",
97    NULL,
98    chap_option_list,
99    NULL,
100    NULL,
101    NULL
102};
103
104chap_state chap[NUM_PPP];		/* CHAP state; one for each unit */
105
106static void ChapChallengeTimeout __P((void *));
107static void ChapResponseTimeout __P((void *));
108static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
109static void ChapRechallenge __P((void *));
110static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
111static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
112static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
113static void ChapSendStatus __P((chap_state *, int));
114static void ChapSendChallenge __P((chap_state *));
115static void ChapSendResponse __P((chap_state *));
116static void ChapGenChallenge __P((chap_state *));
117
118extern double drand48 __P((void));
119extern void srand48 __P((long));
120
121/*
122 * ChapInit - Initialize a CHAP unit.
123 */
124static void
125ChapInit(unit)
126    int unit;
127{
128    chap_state *cstate = &chap[unit];
129
130    BZERO(cstate, sizeof(*cstate));
131    cstate->unit = unit;
132    cstate->clientstate = CHAPCS_INITIAL;
133    cstate->serverstate = CHAPSS_INITIAL;
134    cstate->timeouttime = CHAP_DEFTIMEOUT;
135    cstate->max_transmits = CHAP_DEFTRANSMITS;
136    /* random number generator is initialized in magic_init */
137}
138
139
140/*
141 * ChapAuthWithPeer - Authenticate us with our peer (start client).
142 *
143 */
144void
145ChapAuthWithPeer(unit, our_name, digest)
146    int unit;
147    char *our_name;
148    int digest;
149{
150    chap_state *cstate = &chap[unit];
151
152    cstate->resp_name = our_name;
153    cstate->resp_type = digest;
154
155    if (cstate->clientstate == CHAPCS_INITIAL ||
156	cstate->clientstate == CHAPCS_PENDING) {
157	/* lower layer isn't up - wait until later */
158	cstate->clientstate = CHAPCS_PENDING;
159	return;
160    }
161
162    /*
163     * We get here as a result of LCP coming up.
164     * So even if CHAP was open before, we will
165     * have to re-authenticate ourselves.
166     */
167    cstate->clientstate = CHAPCS_LISTEN;
168}
169
170
171/*
172 * ChapAuthPeer - Authenticate our peer (start server).
173 */
174void
175ChapAuthPeer(unit, our_name, digest)
176    int unit;
177    char *our_name;
178    int digest;
179{
180    chap_state *cstate = &chap[unit];
181
182    cstate->chal_name = our_name;
183    cstate->chal_type = digest;
184
185    if (cstate->serverstate == CHAPSS_INITIAL ||
186	cstate->serverstate == CHAPSS_PENDING) {
187	/* lower layer isn't up - wait until later */
188	cstate->serverstate = CHAPSS_PENDING;
189	return;
190    }
191
192    ChapGenChallenge(cstate);
193    ChapSendChallenge(cstate);		/* crank it up dude! */
194    cstate->serverstate = CHAPSS_INITIAL_CHAL;
195}
196
197
198/*
199 * ChapChallengeTimeout - Timeout expired on sending challenge.
200 */
201static void
202ChapChallengeTimeout(arg)
203    void *arg;
204{
205    chap_state *cstate = (chap_state *) arg;
206
207    /* if we aren't sending challenges, don't worry.  then again we */
208    /* probably shouldn't be here either */
209    if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
210	cstate->serverstate != CHAPSS_RECHALLENGE)
211	return;
212
213    if (cstate->chal_transmits >= cstate->max_transmits) {
214	/* give up on peer */
215	error("Peer failed to respond to CHAP challenge");
216	cstate->serverstate = CHAPSS_BADAUTH;
217	auth_peer_fail(cstate->unit, PPP_CHAP);
218	return;
219    }
220
221    ChapSendChallenge(cstate);		/* Re-send challenge */
222}
223
224
225/*
226 * ChapResponseTimeout - Timeout expired on sending response.
227 */
228static void
229ChapResponseTimeout(arg)
230    void *arg;
231{
232    chap_state *cstate = (chap_state *) arg;
233
234    /* if we aren't sending a response, don't worry. */
235    if (cstate->clientstate != CHAPCS_RESPONSE)
236	return;
237
238    ChapSendResponse(cstate);		/* re-send response */
239}
240
241
242/*
243 * ChapRechallenge - Time to challenge the peer again.
244 */
245static void
246ChapRechallenge(arg)
247    void *arg;
248{
249    chap_state *cstate = (chap_state *) arg;
250
251    /* if we aren't sending a response, don't worry. */
252    if (cstate->serverstate != CHAPSS_OPEN)
253	return;
254
255    ChapGenChallenge(cstate);
256    ChapSendChallenge(cstate);
257    cstate->serverstate = CHAPSS_RECHALLENGE;
258}
259
260
261/*
262 * ChapLowerUp - The lower layer is up.
263 *
264 * Start up if we have pending requests.
265 */
266static void
267ChapLowerUp(unit)
268    int unit;
269{
270    chap_state *cstate = &chap[unit];
271
272    if (cstate->clientstate == CHAPCS_INITIAL)
273	cstate->clientstate = CHAPCS_CLOSED;
274    else if (cstate->clientstate == CHAPCS_PENDING)
275	cstate->clientstate = CHAPCS_LISTEN;
276
277    if (cstate->serverstate == CHAPSS_INITIAL)
278	cstate->serverstate = CHAPSS_CLOSED;
279    else if (cstate->serverstate == CHAPSS_PENDING) {
280	ChapGenChallenge(cstate);
281	ChapSendChallenge(cstate);
282	cstate->serverstate = CHAPSS_INITIAL_CHAL;
283    }
284}
285
286
287/*
288 * ChapLowerDown - The lower layer is down.
289 *
290 * Cancel all timeouts.
291 */
292static void
293ChapLowerDown(unit)
294    int unit;
295{
296    chap_state *cstate = &chap[unit];
297
298    /* Timeout(s) pending?  Cancel if so. */
299    if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
300	cstate->serverstate == CHAPSS_RECHALLENGE)
301	UNTIMEOUT(ChapChallengeTimeout, cstate);
302    else if (cstate->serverstate == CHAPSS_OPEN
303	     && cstate->chal_interval != 0)
304	UNTIMEOUT(ChapRechallenge, cstate);
305    if (cstate->clientstate == CHAPCS_RESPONSE)
306	UNTIMEOUT(ChapResponseTimeout, cstate);
307
308    cstate->clientstate = CHAPCS_INITIAL;
309    cstate->serverstate = CHAPSS_INITIAL;
310}
311
312
313/*
314 * ChapProtocolReject - Peer doesn't grok CHAP.
315 */
316static void
317ChapProtocolReject(unit)
318    int unit;
319{
320    chap_state *cstate = &chap[unit];
321
322    if (cstate->serverstate != CHAPSS_INITIAL &&
323	cstate->serverstate != CHAPSS_CLOSED)
324	auth_peer_fail(unit, PPP_CHAP);
325    if (cstate->clientstate != CHAPCS_INITIAL &&
326	cstate->clientstate != CHAPCS_CLOSED)
327	auth_withpeer_fail(unit, PPP_CHAP);
328    ChapLowerDown(unit);		/* shutdown chap */
329}
330
331
332/*
333 * ChapInput - Input CHAP packet.
334 */
335static void
336ChapInput(unit, inpacket, packet_len)
337    int unit;
338    u_char *inpacket;
339    int packet_len;
340{
341    chap_state *cstate = &chap[unit];
342    u_char *inp;
343    u_char code, id;
344    int len;
345
346    /*
347     * Parse header (code, id and length).
348     * If packet too short, drop it.
349     */
350    inp = inpacket;
351    if (packet_len < CHAP_HEADERLEN) {
352	CHAPDEBUG(("ChapInput: rcvd short header."));
353	return;
354    }
355    GETCHAR(code, inp);
356    GETCHAR(id, inp);
357    GETSHORT(len, inp);
358    if (len < CHAP_HEADERLEN) {
359	CHAPDEBUG(("ChapInput: rcvd illegal length."));
360	return;
361    }
362    if (len > packet_len) {
363	CHAPDEBUG(("ChapInput: rcvd short packet."));
364	return;
365    }
366    len -= CHAP_HEADERLEN;
367
368    /*
369     * Action depends on code (as in fact it usually does :-).
370     */
371    switch (code) {
372    case CHAP_CHALLENGE:
373	ChapReceiveChallenge(cstate, inp, id, len);
374	break;
375
376    case CHAP_RESPONSE:
377	ChapReceiveResponse(cstate, inp, id, len);
378	break;
379
380    case CHAP_FAILURE:
381	ChapReceiveFailure(cstate, inp, id, len);
382	break;
383
384    case CHAP_SUCCESS:
385	ChapReceiveSuccess(cstate, inp, id, len);
386	break;
387
388    default:				/* Need code reject? */
389	warn("Unknown CHAP code (%d) received.", code);
390	break;
391    }
392}
393
394
395/*
396 * ChapReceiveChallenge - Receive Challenge and send Response.
397 */
398static void
399ChapReceiveChallenge(cstate, inp, id, len)
400    chap_state *cstate;
401    u_char *inp;
402    int id;
403    int len;
404{
405    int rchallenge_len;
406    u_char *rchallenge;
407    int secret_len;
408    char secret[MAXSECRETLEN];
409    char rhostname[256];
410    MD5_CTX mdContext;
411    u_char hash[MD5_SIGNATURE_SIZE];
412
413    if (cstate->clientstate == CHAPCS_CLOSED ||
414	cstate->clientstate == CHAPCS_PENDING) {
415	CHAPDEBUG(("ChapReceiveChallenge: in state %d", cstate->clientstate));
416	return;
417    }
418
419    if (len < 2) {
420	CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
421	return;
422    }
423
424    GETCHAR(rchallenge_len, inp);
425    len -= sizeof (u_char) + rchallenge_len;	/* now name field length */
426    if (len < 0) {
427	CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
428	return;
429    }
430    rchallenge = inp;
431    INCPTR(rchallenge_len, inp);
432
433    if (len >= sizeof(rhostname))
434	len = sizeof(rhostname) - 1;
435    BCOPY(inp, rhostname, len);
436    rhostname[len] = '\000';
437
438    /* Microsoft doesn't send their name back in the PPP packet */
439    if (explicit_remote || (remote_name[0] != 0 && rhostname[0] == 0)) {
440	strlcpy(rhostname, remote_name, sizeof(rhostname));
441	CHAPDEBUG(("ChapReceiveChallenge: using '%q' as remote name",
442		   rhostname));
443    }
444
445    /* get secret for authenticating ourselves with the specified host */
446    if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
447		    secret, &secret_len, 0)) {
448	secret_len = 0;		/* assume null secret if can't find one */
449	warn("No CHAP secret found for authenticating us to %q", rhostname);
450    }
451
452    /* cancel response send timeout if necessary */
453    if (cstate->clientstate == CHAPCS_RESPONSE)
454	UNTIMEOUT(ChapResponseTimeout, cstate);
455
456    cstate->resp_id = id;
457    cstate->resp_transmits = 0;
458
459    /*  generate MD based on negotiated type */
460    switch (cstate->resp_type) {
461
462    case CHAP_DIGEST_MD5:
463	MD5Init(&mdContext);
464	MD5Update(&mdContext, &cstate->resp_id, 1);
465	MD5Update(&mdContext, secret, secret_len);
466	MD5Update(&mdContext, rchallenge, rchallenge_len);
467	MD5Final(hash, &mdContext);
468	BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
469	cstate->resp_length = MD5_SIGNATURE_SIZE;
470	break;
471
472#ifdef CHAPMS
473    case CHAP_MICROSOFT:
474	ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
475	break;
476#endif
477
478    default:
479	CHAPDEBUG(("unknown digest type %d", cstate->resp_type));
480	return;
481    }
482
483    BZERO(secret, sizeof(secret));
484    ChapSendResponse(cstate);
485}
486
487
488/*
489 * ChapReceiveResponse - Receive and process response.
490 */
491static void
492ChapReceiveResponse(cstate, inp, id, len)
493    chap_state *cstate;
494    u_char *inp;
495    int id;
496    int len;
497{
498    u_char *remmd, remmd_len;
499    int secret_len, old_state;
500    int code;
501    char rhostname[256];
502    MD5_CTX mdContext;
503    char secret[MAXSECRETLEN];
504    u_char hash[MD5_SIGNATURE_SIZE];
505
506    if (cstate->serverstate == CHAPSS_CLOSED ||
507	cstate->serverstate == CHAPSS_PENDING) {
508	CHAPDEBUG(("ChapReceiveResponse: in state %d", cstate->serverstate));
509	return;
510    }
511
512    if (id != cstate->chal_id)
513	return;			/* doesn't match ID of last challenge */
514
515    /*
516     * If we have received a duplicate or bogus Response,
517     * we have to send the same answer (Success/Failure)
518     * as we did for the first Response we saw.
519     */
520    if (cstate->serverstate == CHAPSS_OPEN) {
521	ChapSendStatus(cstate, CHAP_SUCCESS);
522	return;
523    }
524    if (cstate->serverstate == CHAPSS_BADAUTH) {
525	ChapSendStatus(cstate, CHAP_FAILURE);
526	return;
527    }
528
529    if (len < 2) {
530	CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
531	return;
532    }
533    GETCHAR(remmd_len, inp);		/* get length of MD */
534    remmd = inp;			/* get pointer to MD */
535    INCPTR(remmd_len, inp);
536
537    len -= sizeof (u_char) + remmd_len;
538    if (len < 0) {
539	CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
540	return;
541    }
542
543    UNTIMEOUT(ChapChallengeTimeout, cstate);
544
545    if (len >= sizeof(rhostname))
546	len = sizeof(rhostname) - 1;
547    BCOPY(inp, rhostname, len);
548    rhostname[len] = '\000';
549
550    /*
551     * Get secret for authenticating them with us,
552     * do the hash ourselves, and compare the result.
553     */
554    code = CHAP_FAILURE;
555    if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname),
556		    cstate->chal_name, secret, &secret_len, 1)) {
557	warn("No CHAP secret found for authenticating %q", rhostname);
558    } else {
559
560	/*  generate MD based on negotiated type */
561	switch (cstate->chal_type) {
562
563	case CHAP_DIGEST_MD5:		/* only MD5 is defined for now */
564	    if (remmd_len != MD5_SIGNATURE_SIZE)
565		break;			/* it's not even the right length */
566	    MD5Init(&mdContext);
567	    MD5Update(&mdContext, &cstate->chal_id, 1);
568	    MD5Update(&mdContext, secret, secret_len);
569	    MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
570	    MD5Final(hash, &mdContext);
571
572	    /* compare local and remote MDs and send the appropriate status */
573	    if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
574		code = CHAP_SUCCESS;	/* they are the same! */
575	    break;
576
577	default:
578	    CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
579	}
580    }
581
582    BZERO(secret, sizeof(secret));
583    ChapSendStatus(cstate, code);
584
585    if (code == CHAP_SUCCESS) {
586	old_state = cstate->serverstate;
587	cstate->serverstate = CHAPSS_OPEN;
588	if (old_state == CHAPSS_INITIAL_CHAL) {
589	    auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
590	}
591	if (cstate->chal_interval != 0)
592	    TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
593	notice("CHAP peer authentication succeeded for %q", rhostname);
594
595    } else {
596	error("CHAP peer authentication failed for remote host %q", rhostname);
597	cstate->serverstate = CHAPSS_BADAUTH;
598	auth_peer_fail(cstate->unit, PPP_CHAP);
599    }
600}
601
602/*
603 * ChapReceiveSuccess - Receive Success
604 */
605static void
606ChapReceiveSuccess(cstate, inp, id, len)
607    chap_state *cstate;
608    u_char *inp;
609    u_char id;
610    int len;
611{
612
613    if (cstate->clientstate == CHAPCS_OPEN)
614	/* presumably an answer to a duplicate response */
615	return;
616
617    if (cstate->clientstate != CHAPCS_RESPONSE) {
618	/* don't know what this is */
619	CHAPDEBUG(("ChapReceiveSuccess: in state %d\n", cstate->clientstate));
620	return;
621    }
622
623    UNTIMEOUT(ChapResponseTimeout, cstate);
624
625    /*
626     * Print message.
627     */
628    if (len > 0)
629	PRINTMSG(inp, len);
630
631    cstate->clientstate = CHAPCS_OPEN;
632
633    auth_withpeer_success(cstate->unit, PPP_CHAP);
634}
635
636
637/*
638 * ChapReceiveFailure - Receive failure.
639 */
640static void
641ChapReceiveFailure(cstate, inp, id, len)
642    chap_state *cstate;
643    u_char *inp;
644    u_char id;
645    int len;
646{
647    if (cstate->clientstate != CHAPCS_RESPONSE) {
648	/* don't know what this is */
649	CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate->clientstate));
650	return;
651    }
652
653    UNTIMEOUT(ChapResponseTimeout, cstate);
654
655    /*
656     * Print message.
657     */
658    if (len > 0)
659	PRINTMSG(inp, len);
660
661    error("CHAP authentication failed");
662    auth_withpeer_fail(cstate->unit, PPP_CHAP);
663}
664
665
666/*
667 * ChapSendChallenge - Send an Authenticate challenge.
668 */
669static void
670ChapSendChallenge(cstate)
671    chap_state *cstate;
672{
673    u_char *outp;
674    int chal_len, name_len;
675    int outlen;
676
677    chal_len = cstate->chal_len;
678    name_len = strlen(cstate->chal_name);
679    outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
680    outp = outpacket_buf;
681
682    MAKEHEADER(outp, PPP_CHAP);		/* paste in a CHAP header */
683
684    PUTCHAR(CHAP_CHALLENGE, outp);
685    PUTCHAR(cstate->chal_id, outp);
686    PUTSHORT(outlen, outp);
687
688    PUTCHAR(chal_len, outp);		/* put length of challenge */
689    BCOPY(cstate->challenge, outp, chal_len);
690    INCPTR(chal_len, outp);
691
692    BCOPY(cstate->chal_name, outp, name_len);	/* append hostname */
693
694    output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
695
696    TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
697    ++cstate->chal_transmits;
698}
699
700
701/*
702 * ChapSendStatus - Send a status response (ack or nak).
703 */
704static void
705ChapSendStatus(cstate, code)
706    chap_state *cstate;
707    int code;
708{
709    u_char *outp;
710    int outlen, msglen;
711    char msg[256];
712
713    if (code == CHAP_SUCCESS)
714	slprintf(msg, sizeof(msg), "Welcome to %s.", hostname);
715    else
716	slprintf(msg, sizeof(msg), "I don't like you.  Go 'way.");
717    msglen = strlen(msg);
718
719    outlen = CHAP_HEADERLEN + msglen;
720    outp = outpacket_buf;
721
722    MAKEHEADER(outp, PPP_CHAP);	/* paste in a header */
723
724    PUTCHAR(code, outp);
725    PUTCHAR(cstate->chal_id, outp);
726    PUTSHORT(outlen, outp);
727    BCOPY(msg, outp, msglen);
728    output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
729}
730
731/*
732 * ChapGenChallenge is used to generate a pseudo-random challenge string of
733 * a pseudo-random length between min_len and max_len.  The challenge
734 * string and its length are stored in *cstate, and various other fields of
735 * *cstate are initialized.
736 */
737
738static void
739ChapGenChallenge(cstate)
740    chap_state *cstate;
741{
742    int chal_len;
743    u_char *ptr = cstate->challenge;
744    int i;
745
746    /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
747       MAX_CHALLENGE_LENGTH */
748    chal_len =  (unsigned) ((drand48() *
749			     (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
750			    MIN_CHALLENGE_LENGTH);
751    cstate->chal_len = chal_len;
752    cstate->chal_id = ++cstate->id;
753    cstate->chal_transmits = 0;
754
755    /* generate a random string */
756    for (i = 0; i < chal_len; i++)
757	*ptr++ = (char) (drand48() * 0xff);
758}
759
760/*
761 * ChapSendResponse - send a response packet with values as specified
762 * in *cstate.
763 */
764/* ARGSUSED */
765static void
766ChapSendResponse(cstate)
767    chap_state *cstate;
768{
769    u_char *outp;
770    int outlen, md_len, name_len;
771
772    md_len = cstate->resp_length;
773    name_len = strlen(cstate->resp_name);
774    outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
775    outp = outpacket_buf;
776
777    MAKEHEADER(outp, PPP_CHAP);
778
779    PUTCHAR(CHAP_RESPONSE, outp);	/* we are a response */
780    PUTCHAR(cstate->resp_id, outp);	/* copy id from challenge packet */
781    PUTSHORT(outlen, outp);		/* packet length */
782
783    PUTCHAR(md_len, outp);		/* length of MD */
784    BCOPY(cstate->response, outp, md_len);	/* copy MD to buffer */
785    INCPTR(md_len, outp);
786
787    BCOPY(cstate->resp_name, outp, name_len); /* append our name */
788
789    /* send the packet */
790    output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
791
792    cstate->clientstate = CHAPCS_RESPONSE;
793    TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
794    ++cstate->resp_transmits;
795}
796
797/*
798 * ChapPrintPkt - print the contents of a CHAP packet.
799 */
800static char *ChapCodenames[] = {
801    "Challenge", "Response", "Success", "Failure"
802};
803
804static int
805ChapPrintPkt(p, plen, printer, arg)
806    u_char *p;
807    int plen;
808    void (*printer) __P((void *, char *, ...));
809    void *arg;
810{
811    int code, id, len;
812    int clen, nlen;
813    u_char x;
814
815    if (plen < CHAP_HEADERLEN)
816	return 0;
817    GETCHAR(code, p);
818    GETCHAR(id, p);
819    GETSHORT(len, p);
820    if (len < CHAP_HEADERLEN || len > plen)
821	return 0;
822
823    if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
824	printer(arg, " %s", ChapCodenames[code-1]);
825    else
826	printer(arg, " code=0x%x", code);
827    printer(arg, " id=0x%x", id);
828    len -= CHAP_HEADERLEN;
829    switch (code) {
830    case CHAP_CHALLENGE:
831    case CHAP_RESPONSE:
832	if (len < 1)
833	    break;
834	clen = p[0];
835	if (len < clen + 1)
836	    break;
837	++p;
838	nlen = len - clen - 1;
839	printer(arg, " <");
840	for (; clen > 0; --clen) {
841	    GETCHAR(x, p);
842	    printer(arg, "%.2x", x);
843	}
844	printer(arg, ">, name = ");
845	print_string((char *)p, nlen, printer, arg);
846	break;
847    case CHAP_FAILURE:
848    case CHAP_SUCCESS:
849	printer(arg, " ");
850	print_string((char *)p, len, printer, arg);
851	break;
852    default:
853	for (clen = len; clen > 0; --clen) {
854	    GETCHAR(x, p);
855	    printer(arg, " %.2x", x);
856	}
857    }
858
859    return len + CHAP_HEADERLEN;
860}
861