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