xref: /freebsd/usr.sbin/ppp/chap.c (revision a8445737e740901f5f2c8d24c12ef7fc8b00134e)
1 /*
2  *			PPP CHAP Module
3  *
4  *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5  *
6  *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
7  *
8  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the Internet Initiative Japan, Inc.  The name of the
14  * IIJ may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  *
20  * $Id: chap.c,v 1.36 1998/08/07 18:42:47 brian Exp $
21  *
22  *	TODO:
23  */
24 #include <sys/types.h>
25 #include <netinet/in.h>
26 #include <netinet/in_systm.h>
27 #include <netinet/ip.h>
28 #include <sys/un.h>
29 
30 #ifdef HAVE_DES
31 #include <md4.h>
32 #endif
33 #include <md5.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <termios.h>
37 
38 #include "mbuf.h"
39 #include "log.h"
40 #include "defs.h"
41 #include "timer.h"
42 #include "fsm.h"
43 #include "lcpproto.h"
44 #include "lcp.h"
45 #include "lqr.h"
46 #include "hdlc.h"
47 #include "auth.h"
48 #include "chap.h"
49 #include "async.h"
50 #include "throughput.h"
51 #include "descriptor.h"
52 #include "iplist.h"
53 #include "slcompress.h"
54 #include "ipcp.h"
55 #include "filter.h"
56 #include "ccp.h"
57 #include "link.h"
58 #include "physical.h"
59 #include "mp.h"
60 #include "bundle.h"
61 #include "chat.h"
62 #include "cbcp.h"
63 #include "datalink.h"
64 #ifdef HAVE_DES
65 #include "chap_ms.h"
66 #endif
67 
68 static const char *chapcodes[] = {
69   "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
70 };
71 
72 static void
73 ChapOutput(struct physical *physical, u_int code, u_int id,
74 	   const u_char * ptr, int count, const char *text)
75 {
76   int plen;
77   struct fsmheader lh;
78   struct mbuf *bp;
79 
80   plen = sizeof(struct fsmheader) + count;
81   lh.code = code;
82   lh.id = id;
83   lh.length = htons(plen);
84   bp = mbuf_Alloc(plen, MB_FSM);
85   memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
86   if (count)
87     memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
88   log_DumpBp(LogDEBUG, "ChapOutput", bp);
89   if (text == NULL)
90     log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]);
91   else
92     log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text);
93   hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp);
94 }
95 
96 void
97 chap_SendChallenge(struct authinfo *auth, int chapid, struct physical *physical)
98 {
99   struct chap *chap = auth2chap(auth);
100   int len, i;
101   char *cp;
102 
103   randinit();
104   cp = chap->challenge_data;
105   *cp++ = chap->challenge_len = random() % 32 + 16;
106   for (i = 0; i < chap->challenge_len; i++)
107     *cp++ = random() & 0xff;
108   len = strlen(physical->dl->bundle->cfg.auth.name);
109   memcpy(cp, physical->dl->bundle->cfg.auth.name, len);
110   cp += len;
111   ChapOutput(physical, CHAP_CHALLENGE, chapid, chap->challenge_data,
112 	     cp - chap->challenge_data, NULL);
113 }
114 
115 static void
116 RecvChapTalk(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
117              struct physical *physical)
118 {
119   int valsize, len;
120   int arglen, keylen, namelen;
121   char *cp, *argp, *ap, *name, *digest;
122   char *keyp;
123   MD5_CTX MD5context;		/* context for MD5 */
124   char answer[100];
125   char cdigest[16];
126 #ifdef HAVE_DES
127   int ix;
128   MD4_CTX MD4context;		/* context for MD4 */
129 #endif
130 
131   len = ntohs(chp->length);
132   log_Printf(LogDEBUG, "RecvChapTalk: length: %d\n", len);
133   arglen = len - sizeof(struct fsmheader);
134   cp = (char *) MBUF_CTOP(bp);
135   valsize = *cp++ & 255;
136   name = cp + valsize;
137   namelen = arglen - valsize - 1;
138   name[namelen] = 0;
139 
140   log_Printf(LogPHASE, "Chap Input: %s (from %s)\n",
141              chapcodes[chp->code], name);
142 
143   switch (chp->code) {
144   case CHAP_CHALLENGE:
145     keyp = bundle->cfg.auth.key;
146     keylen = strlen(bundle->cfg.auth.key);
147     name = bundle->cfg.auth.name;
148     namelen = strlen(bundle->cfg.auth.name);
149 
150 #ifdef HAVE_DES
151     if (physical->dl->chap.using_MSChap)
152       argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN);
153     else
154 #endif
155       argp = malloc(1 + valsize + namelen + 16);
156 
157     if (argp == NULL) {
158       ChapOutput(physical, CHAP_FAILURE, chp->id, "Out of memory!", 14, NULL);
159       return;
160     }
161 #ifdef HAVE_DES
162     if (physical->dl->chap.using_MSChap) {
163       digest = argp;     /* this is the response */
164       *digest++ = MS_CHAP_RESPONSE_LEN;   /* 49 */
165       memset(digest, '\0', 24);
166       digest += 24;
167 
168       ap = answer;       /* this is the challenge */
169       memcpy(ap, keyp, keylen);
170       ap += 2 * keylen;
171       memcpy(ap, cp, valsize);
172       log_DumpBuff(LogDEBUG, "recv", ap, valsize);
173       ap += valsize;
174       for (ix = keylen; ix > 0 ; ix--) {
175           answer[2*ix-2] = answer[ix-1];
176           answer[2*ix-1] = 0;
177       }
178       MD4Init(&MD4context);
179       MD4Update(&MD4context, answer, 2 * keylen);
180       MD4Final(digest, &MD4context);
181       memcpy(digest + 25, name, namelen);
182       ap += 2 * keylen;
183       chap_MS(digest, answer + 2 * keylen, valsize);
184       log_DumpBuff(LogDEBUG, "answer", digest, 24);
185       ChapOutput(physical, CHAP_RESPONSE, chp->id, argp,
186 		 namelen + MS_CHAP_RESPONSE_LEN + 1, name);
187     } else {
188 #endif
189       digest = argp;
190       *digest++ = 16;		/* value size */
191       ap = answer;
192       *ap++ = chp->id;
193       memcpy(ap, keyp, keylen);
194       ap += keylen;
195       memcpy(ap, cp, valsize);
196       log_DumpBuff(LogDEBUG, "recv", ap, valsize);
197       ap += valsize;
198       MD5Init(&MD5context);
199       MD5Update(&MD5context, answer, ap - answer);
200       MD5Final(digest, &MD5context);
201       log_DumpBuff(LogDEBUG, "answer", digest, 16);
202       memcpy(digest + 16, name, namelen);
203       ap += namelen;
204       /* Send answer to the peer */
205       ChapOutput(physical, CHAP_RESPONSE, chp->id, argp, namelen + 17, name);
206 #ifdef HAVE_DES
207     }
208 #endif
209     free(argp);
210     if (*name == '\0')
211       log_Printf(LogWARN, "Sending empty CHAP authname!\n");
212     break;
213   case CHAP_RESPONSE:
214     /*
215      * Get a secret key corresponds to the peer
216      */
217     keyp = auth_GetSecret(bundle, name, namelen, physical);
218     if (keyp) {
219       /*
220        * Compute correct digest value
221        */
222       keylen = strlen(keyp);
223       ap = answer;
224       *ap++ = chp->id;
225       memcpy(ap, keyp, keylen);
226       ap += keylen;
227       MD5Init(&MD5context);
228       MD5Update(&MD5context, answer, ap - answer);
229       MD5Update(&MD5context, physical->dl->chap.challenge_data + 1,
230                 physical->dl->chap.challenge_len);
231       MD5Final(cdigest, &MD5context);
232       log_DumpBuff(LogDEBUG, "got", cp, 16);
233       log_DumpBuff(LogDEBUG, "expect", cdigest, 16);
234 
235       /*
236        * Compare with the response
237        */
238       if (memcmp(cp, cdigest, 16) == 0) {
239         datalink_GotAuthname(physical->dl, name, namelen);
240 	ChapOutput(physical, CHAP_SUCCESS, chp->id, "Welcome!!", 10, NULL);
241 	physical->link.lcp.auth_ineed = 0;
242         if (Enabled(bundle, OPT_UTMP))
243           physical_Login(physical, name);
244 
245         if (physical->link.lcp.auth_iwait == 0)
246           /*
247            * Either I didn't need to authenticate, or I've already been
248            * told that I got the answer right.
249            */
250           datalink_AuthOk(physical->dl);
251 
252 	break;
253       }
254     }
255 
256     /*
257      * Peer is not registerd, or response digest is wrong.
258      */
259     ChapOutput(physical, CHAP_FAILURE, chp->id, "Invalid!!", 9, NULL);
260     datalink_AuthNotOk(physical->dl);
261     break;
262   }
263 }
264 
265 static void
266 RecvChapResult(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
267 	       struct physical *physical)
268 {
269   int len;
270 
271   len = ntohs(chp->length);
272   log_Printf(LogDEBUG, "RecvChapResult: length: %d\n", len);
273   if (chp->code == CHAP_SUCCESS) {
274     if (physical->link.lcp.auth_iwait == PROTO_CHAP) {
275       physical->link.lcp.auth_iwait = 0;
276       if (physical->link.lcp.auth_ineed == 0)
277         /*
278          * We've succeeded in our ``login''
279          * If we're not expecting  the peer to authenticate (or he already
280          * has), proceed to network phase.
281          */
282         datalink_AuthOk(physical->dl);
283     }
284   } else {
285     /* CHAP failed - it's not going to get any better */
286     log_Printf(LogPHASE, "Chap Input: Giving up after name/key FAILURE\n");
287     datalink_AuthNotOk(physical->dl);
288   }
289 }
290 
291 void
292 chap_Input(struct bundle *bundle, struct mbuf *bp, struct physical *physical)
293 {
294   int len = mbuf_Length(bp);
295   struct fsmheader *chp;
296 
297   if (len >= sizeof(struct fsmheader)) {
298     chp = (struct fsmheader *) MBUF_CTOP(bp);
299     if (len >= ntohs(chp->length)) {
300       if (chp->code < 1 || chp->code > 4)
301 	chp->code = 0;
302       bp->offset += sizeof(struct fsmheader);
303       bp->cnt -= sizeof(struct fsmheader);
304 
305       switch (chp->code) {
306       case CHAP_RESPONSE:
307 	auth_StopTimer(&physical->dl->chap.auth);
308 	/* Fall into.. */
309       case CHAP_CHALLENGE:
310 	RecvChapTalk(bundle, chp, bp, physical);
311 	break;
312       case CHAP_SUCCESS:
313       case CHAP_FAILURE:
314         log_Printf(LogPHASE, "Chap Input: %s\n", chapcodes[chp->code]);
315 	RecvChapResult(bundle, chp, bp, physical);
316 	break;
317       }
318     }
319   }
320   mbuf_Free(bp);
321 }
322