xref: /freebsd/usr.sbin/ppp/chap.c (revision 0de89efe5c443f213c7ea28773ef2dc6cf3af2ed)
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.22 1997/09/22 23:59:13 brian Exp $
21  *
22  *	TODO:
23  */
24 #include <sys/types.h>
25 #include <time.h>
26 #include <utmp.h>
27 #include <ctype.h>
28 
29 #include "fsm.h"
30 #include "chap.h"
31 #include "chap_ms.h"
32 #include "lcpproto.h"
33 #include "lcp.h"
34 #include "hdlc.h"
35 #include "phase.h"
36 #include "loadalias.h"
37 #include "vars.h"
38 #include "auth.h"
39 #ifdef __OpenBSD__
40 #include "util.h"
41 #else
42 #include "libutil.h"
43 #endif
44 
45 static char *chapcodes[] = {
46   "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
47 };
48 
49 struct authinfo AuthChapInfo = {
50   SendChapChallenge,
51 };
52 
53 extern char *AuthGetSecret();
54 extern int randinit;
55 
56 void
57 ChapOutput(u_int code, u_int id, u_char * ptr, int count)
58 {
59   int plen;
60   struct fsmheader lh;
61   struct mbuf *bp;
62 
63   plen = sizeof(struct fsmheader) + count;
64   lh.code = code;
65   lh.id = id;
66   lh.length = htons(plen);
67   bp = mballoc(plen, MB_FSM);
68   bcopy(&lh, MBUF_CTOP(bp), sizeof(struct fsmheader));
69   if (count)
70     bcopy(ptr, MBUF_CTOP(bp) + sizeof(struct fsmheader), count);
71   LogDumpBp(LogDEBUG, "ChapOutput", bp);
72   LogPrintf(LogLCP, "ChapOutput: %s\n", chapcodes[code]);
73   HdlcOutput(PRI_LINK, PROTO_CHAP, bp);
74 }
75 
76 
77 static char challenge_data[80];
78 static int challenge_len;
79 
80 void
81 SendChapChallenge(int chapid)
82 {
83   int len, i;
84   char *cp;
85 
86   if (!randinit) {
87     randinit = 1;
88     srandomdev();
89   }
90   cp = challenge_data;
91   *cp++ = challenge_len = random() % 32 + 16;
92   for (i = 0; i < challenge_len; i++)
93     *cp++ = random() & 0xff;
94   len = strlen(VarAuthName);
95   bcopy(VarAuthName, cp, len);
96   cp += len;
97   ChapOutput(CHAP_CHALLENGE, chapid, challenge_data, cp - challenge_data);
98 }
99 
100 void
101 RecvChapTalk(struct fsmheader * chp, struct mbuf * bp)
102 {
103   int valsize, len;
104   int arglen, keylen, namelen;
105   char *cp, *argp, *ap, *name, *digest;
106   char *keyp;
107   MD5_CTX MD5context;		/* context for MD5 */
108   char answer[100];
109   char cdigest[16];
110 #ifdef HAVE_DES
111   int ix;
112   MD4_CTX MD4context;		/* context for MD4 */
113 #endif
114 
115   len = ntohs(chp->length);
116   LogPrintf(LogDEBUG, "RecvChapTalk: length: %d\n", len);
117   arglen = len - sizeof(struct fsmheader);
118   cp = (char *) MBUF_CTOP(bp);
119   valsize = *cp++ & 255;
120   name = cp + valsize;
121   namelen = arglen - valsize - 1;
122   name[namelen] = 0;
123   LogPrintf(LogPHASE, " Valsize = %d, Name = %s\n", valsize, name);
124 
125   /*
126    * Get a secret key corresponds to the peer
127    */
128   keyp = AuthGetSecret(SECRETFILE, name, namelen, chp->code == CHAP_RESPONSE);
129 
130   switch (chp->code) {
131   case CHAP_CHALLENGE:
132     if (keyp) {
133       keylen = strlen(keyp);
134     } else {
135       keylen = strlen(VarAuthKey);
136       keyp = VarAuthKey;
137     }
138     name = VarAuthName;
139     namelen = strlen(VarAuthName);
140 
141 #ifdef HAVE_DES
142     if (VarMSChap)
143       argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN);
144     else
145 #endif
146       argp = malloc(1 + valsize + namelen + 16);
147 
148     if (argp == NULL) {
149       ChapOutput(CHAP_FAILURE, chp->id, "Out of memory!", 14);
150       return;
151     }
152 #ifdef HAVE_DES
153     if (VarMSChap) {
154       digest = argp;     /* this is the response */
155       *digest++ = MS_CHAP_RESPONSE_LEN;   /* 49 */
156       bzero(digest, 24); digest += 24;
157 
158       ap = answer;       /* this is the challenge */
159       bcopy(keyp, ap, keylen);
160       ap += 2 * keylen;
161       bcopy(cp, ap, valsize);
162       LogDumpBuff(LogDEBUG, "recv", ap, valsize);
163       ap += valsize;
164       for (ix = keylen; ix > 0 ; ix--) {
165           answer[2*ix-2] = answer[ix-1];
166           answer[2*ix-1] = 0;
167       }
168       MD4Init(&MD4context);
169       MD4Update(&MD4context, answer, 2 * keylen);
170       MD4Final(digest, &MD4context);
171       bcopy(name, digest + 25, namelen);
172       ap += 2 * keylen;
173       ChapMS(digest, answer + 2 * keylen, valsize);
174       LogDumpBuff(LogDEBUG, "answer", digest, 24);
175       ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + MS_CHAP_RESPONSE_LEN + 1);
176     } else {
177 #endif
178       digest = argp;
179       *digest++ = 16;		/* value size */
180       ap = answer;
181       *ap++ = chp->id;
182       bcopy(keyp, ap, keylen);
183       ap += keylen;
184       bcopy(cp, ap, valsize);
185       LogDumpBuff(LogDEBUG, "recv", ap, valsize);
186       ap += valsize;
187       MD5Init(&MD5context);
188       MD5Update(&MD5context, answer, ap - answer);
189       MD5Final(digest, &MD5context);
190       LogDumpBuff(LogDEBUG, "answer", digest, 16);
191       bcopy(name, digest + 16, namelen);
192       ap += namelen;
193       /* Send answer to the peer */
194       ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + 17);
195 #ifdef HAVE_DES
196     }
197 #endif
198     free(argp);
199     break;
200   case CHAP_RESPONSE:
201     if (keyp) {
202 
203       /*
204        * Compute correct digest value
205        */
206       keylen = strlen(keyp);
207       ap = answer;
208       *ap++ = chp->id;
209       bcopy(keyp, ap, keylen);
210       ap += keylen;
211       MD5Init(&MD5context);
212       MD5Update(&MD5context, answer, ap - answer);
213       MD5Update(&MD5context, challenge_data + 1, challenge_len);
214       MD5Final(cdigest, &MD5context);
215       LogDumpBuff(LogDEBUG, "got", cp, 16);
216       LogDumpBuff(LogDEBUG, "expect", cdigest, 16);
217 
218       /*
219        * Compare with the response
220        */
221       if (bcmp(cp, cdigest, 16) == 0) {
222 	ChapOutput(CHAP_SUCCESS, chp->id, "Welcome!!", 10);
223         if ((mode & MODE_DIRECT) && isatty(modem) && Enabled(ConfUtmp))
224 	  if (Utmp)
225 	    LogPrintf(LogERROR, "Oops, already logged in on %s\n",
226 		      VarBaseDevice);
227 	  else {
228 	    struct utmp ut;
229 	    memset(&ut, 0, sizeof(ut));
230 	    time(&ut.ut_time);
231 	    strncpy(ut.ut_name, name, sizeof(ut.ut_name)-1);
232 	    strncpy(ut.ut_line, VarBaseDevice, sizeof(ut.ut_line)-1);
233 	    if (logout(ut.ut_line))
234 	      logwtmp(ut.ut_line, "", "");
235 	    login(&ut);
236 	    Utmp = 1;
237 	  }
238 	NewPhase(PHASE_NETWORK);
239 	break;
240       }
241     }
242 
243     /*
244      * Peer is not registerd, or response digest is wrong.
245      */
246     ChapOutput(CHAP_FAILURE, chp->id, "Invalid!!", 9);
247     reconnect(RECON_FALSE);
248     LcpClose();
249     break;
250   }
251 }
252 
253 void
254 RecvChapResult(struct fsmheader * chp, struct mbuf * bp)
255 {
256   int len;
257   struct lcpstate *lcp = &LcpInfo;
258 
259   len = ntohs(chp->length);
260   LogPrintf(LogDEBUG, "RecvChapResult: length: %d\n", len);
261   if (chp->code == CHAP_SUCCESS) {
262     if (lcp->auth_iwait == PROTO_CHAP) {
263       lcp->auth_iwait = 0;
264       if (lcp->auth_ineed == 0)
265 	NewPhase(PHASE_NETWORK);
266     }
267   } else {
268 
269     /*
270      * Maybe, we shoud close LCP. Of cause, peer may take close action, too.
271      */
272     ;
273   }
274 }
275 
276 void
277 ChapInput(struct mbuf * bp)
278 {
279   int len = plength(bp);
280   struct fsmheader *chp;
281 
282   if (len >= sizeof(struct fsmheader)) {
283     chp = (struct fsmheader *) MBUF_CTOP(bp);
284     if (len >= ntohs(chp->length)) {
285       if (chp->code < 1 || chp->code > 4)
286 	chp->code = 0;
287       LogPrintf(LogLCP, "ChapInput: %s\n", chapcodes[chp->code]);
288 
289       bp->offset += sizeof(struct fsmheader);
290       bp->cnt -= sizeof(struct fsmheader);
291 
292       switch (chp->code) {
293       case CHAP_RESPONSE:
294 	StopAuthTimer(&AuthChapInfo);
295 	/* Fall into.. */
296       case CHAP_CHALLENGE:
297 	RecvChapTalk(chp, bp);
298 	break;
299       case CHAP_SUCCESS:
300       case CHAP_FAILURE:
301 	RecvChapResult(chp, bp);
302 	break;
303       }
304     }
305   }
306   pfree(bp);
307 }
308