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