chap.c revision 37192
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.32 1998/05/21 22:55:02 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 <termios.h>
36
37#include "mbuf.h"
38#include "log.h"
39#include "defs.h"
40#include "timer.h"
41#include "fsm.h"
42#include "lcpproto.h"
43#include "lcp.h"
44#include "lqr.h"
45#include "hdlc.h"
46#include "auth.h"
47#include "chap.h"
48#include "async.h"
49#include "throughput.h"
50#include "descriptor.h"
51#include "iplist.h"
52#include "slcompress.h"
53#include "ipcp.h"
54#include "filter.h"
55#include "ccp.h"
56#include "link.h"
57#include "physical.h"
58#include "mp.h"
59#include "bundle.h"
60#include "chat.h"
61#include "datalink.h"
62#ifdef HAVE_DES
63#include "chap_ms.h"
64#endif
65
66static const char *chapcodes[] = {
67  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
68};
69
70static void
71ChapOutput(struct physical *physical, u_int code, u_int id,
72	   const u_char * ptr, int count)
73{
74  int plen;
75  struct fsmheader lh;
76  struct mbuf *bp;
77
78  plen = sizeof(struct fsmheader) + count;
79  lh.code = code;
80  lh.id = id;
81  lh.length = htons(plen);
82  bp = mbuf_Alloc(plen, MB_FSM);
83  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
84  if (count)
85    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
86  log_DumpBp(LogDEBUG, "ChapOutput", bp);
87  log_Printf(LogLCP, "ChapOutput: %s\n", chapcodes[code]);
88  hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp);
89}
90
91void
92chap_SendChallenge(struct authinfo *auth, int chapid, struct physical *physical)
93{
94  struct chap *chap = auth2chap(auth);
95  int len, i;
96  char *cp;
97
98  randinit();
99  cp = chap->challenge_data;
100  *cp++ = chap->challenge_len = random() % 32 + 16;
101  for (i = 0; i < chap->challenge_len; i++)
102    *cp++ = random() & 0xff;
103  len = strlen(physical->dl->bundle->cfg.auth.name);
104  memcpy(cp, physical->dl->bundle->cfg.auth.name, len);
105  cp += len;
106  ChapOutput(physical, CHAP_CHALLENGE, chapid, chap->challenge_data,
107	     cp - chap->challenge_data);
108}
109
110static void
111RecvChapTalk(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
112             struct physical *physical)
113{
114  int valsize, len;
115  int arglen, keylen, namelen;
116  char *cp, *argp, *ap, *name, *digest;
117  char *keyp;
118  MD5_CTX MD5context;		/* context for MD5 */
119  char answer[100];
120  char cdigest[16];
121#ifdef HAVE_DES
122  int ix;
123  MD4_CTX MD4context;		/* context for MD4 */
124#endif
125
126  len = ntohs(chp->length);
127  log_Printf(LogDEBUG, "RecvChapTalk: length: %d\n", len);
128  arglen = len - sizeof(struct fsmheader);
129  cp = (char *) MBUF_CTOP(bp);
130  valsize = *cp++ & 255;
131  name = cp + valsize;
132  namelen = arglen - valsize - 1;
133  name[namelen] = 0;
134  log_Printf(LogLCP, " Valsize = %d, Name = \"%s\"\n", valsize, name);
135
136  switch (chp->code) {
137  case CHAP_CHALLENGE:
138    keyp = bundle->cfg.auth.key;
139    keylen = strlen(bundle->cfg.auth.key);
140    name = bundle->cfg.auth.name;
141    namelen = strlen(bundle->cfg.auth.name);
142
143#ifdef HAVE_DES
144    if (physical->dl->chap.using_MSChap)
145      argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN);
146    else
147#endif
148      argp = malloc(1 + valsize + namelen + 16);
149
150    if (argp == NULL) {
151      ChapOutput(physical, CHAP_FAILURE, chp->id, "Out of memory!", 14);
152      return;
153    }
154#ifdef HAVE_DES
155    if (physical->dl->chap.using_MSChap) {
156      digest = argp;     /* this is the response */
157      *digest++ = MS_CHAP_RESPONSE_LEN;   /* 49 */
158      memset(digest, '\0', 24);
159      digest += 24;
160
161      ap = answer;       /* this is the challenge */
162      memcpy(ap, keyp, keylen);
163      ap += 2 * keylen;
164      memcpy(ap, cp, valsize);
165      log_DumpBuff(LogDEBUG, "recv", ap, valsize);
166      ap += valsize;
167      for (ix = keylen; ix > 0 ; ix--) {
168          answer[2*ix-2] = answer[ix-1];
169          answer[2*ix-1] = 0;
170      }
171      MD4Init(&MD4context);
172      MD4Update(&MD4context, answer, 2 * keylen);
173      MD4Final(digest, &MD4context);
174      memcpy(digest + 25, name, namelen);
175      ap += 2 * keylen;
176      chap_MS(digest, answer + 2 * keylen, valsize);
177      log_DumpBuff(LogDEBUG, "answer", digest, 24);
178      ChapOutput(physical, CHAP_RESPONSE, chp->id, argp,
179		 namelen + MS_CHAP_RESPONSE_LEN + 1);
180    } else {
181#endif
182      digest = argp;
183      *digest++ = 16;		/* value size */
184      ap = answer;
185      *ap++ = chp->id;
186      memcpy(ap, keyp, keylen);
187      ap += keylen;
188      memcpy(ap, cp, valsize);
189      log_DumpBuff(LogDEBUG, "recv", ap, valsize);
190      ap += valsize;
191      MD5Init(&MD5context);
192      MD5Update(&MD5context, answer, ap - answer);
193      MD5Final(digest, &MD5context);
194      log_DumpBuff(LogDEBUG, "answer", digest, 16);
195      memcpy(digest + 16, name, namelen);
196      ap += namelen;
197      /* Send answer to the peer */
198      ChapOutput(physical, CHAP_RESPONSE, chp->id, argp, namelen + 17);
199#ifdef HAVE_DES
200    }
201#endif
202    free(argp);
203    break;
204  case CHAP_RESPONSE:
205    /*
206     * Get a secret key corresponds to the peer
207     */
208    keyp = auth_GetSecret(bundle, name, namelen, physical);
209    if (keyp) {
210      /*
211       * Compute correct digest value
212       */
213      keylen = strlen(keyp);
214      ap = answer;
215      *ap++ = chp->id;
216      memcpy(ap, keyp, keylen);
217      ap += keylen;
218      MD5Init(&MD5context);
219      MD5Update(&MD5context, answer, ap - answer);
220      MD5Update(&MD5context, physical->dl->chap.challenge_data + 1,
221                physical->dl->chap.challenge_len);
222      MD5Final(cdigest, &MD5context);
223      log_DumpBuff(LogDEBUG, "got", cp, 16);
224      log_DumpBuff(LogDEBUG, "expect", cdigest, 16);
225
226      /*
227       * Compare with the response
228       */
229      if (memcmp(cp, cdigest, 16) == 0) {
230        datalink_GotAuthname(physical->dl, name, namelen);
231	ChapOutput(physical, CHAP_SUCCESS, chp->id, "Welcome!!", 10);
232        if (Enabled(bundle, OPT_UTMP))
233          physical_Login(physical, name);
234
235        if (physical->link.lcp.auth_iwait == 0)
236          /*
237           * Either I didn't need to authenticate, or I've already been
238           * told that I got the answer right.
239           */
240          datalink_AuthOk(physical->dl);
241
242	break;
243      }
244    }
245
246    /*
247     * Peer is not registerd, or response digest is wrong.
248     */
249    ChapOutput(physical, CHAP_FAILURE, chp->id, "Invalid!!", 9);
250    datalink_AuthNotOk(physical->dl);
251    break;
252  }
253}
254
255static void
256RecvChapResult(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
257	       struct physical *physical)
258{
259  int len;
260
261  len = ntohs(chp->length);
262  log_Printf(LogDEBUG, "RecvChapResult: length: %d\n", len);
263  if (chp->code == CHAP_SUCCESS) {
264    if (physical->link.lcp.auth_iwait == PROTO_CHAP) {
265      physical->link.lcp.auth_iwait = 0;
266      if (physical->link.lcp.auth_ineed == 0)
267        /*
268         * We've succeeded in our ``login''
269         * If we're not expecting  the peer to authenticate (or he already
270         * has), proceed to network phase.
271         */
272        datalink_AuthOk(physical->dl);
273    }
274  } else {
275    /* CHAP failed - it's not going to get any better */
276    log_Printf(LogPHASE, "Received CHAP_FAILURE\n");
277    datalink_AuthNotOk(physical->dl);
278  }
279}
280
281void
282chap_Input(struct bundle *bundle, struct mbuf *bp, struct physical *physical)
283{
284  int len = mbuf_Length(bp);
285  struct fsmheader *chp;
286
287  if (len >= sizeof(struct fsmheader)) {
288    chp = (struct fsmheader *) MBUF_CTOP(bp);
289    if (len >= ntohs(chp->length)) {
290      if (chp->code < 1 || chp->code > 4)
291	chp->code = 0;
292      log_Printf(LogLCP, "chap_Input: %s\n", chapcodes[chp->code]);
293
294      bp->offset += sizeof(struct fsmheader);
295      bp->cnt -= sizeof(struct fsmheader);
296
297      switch (chp->code) {
298      case CHAP_RESPONSE:
299	auth_StopTimer(&physical->dl->chap.auth);
300	/* Fall into.. */
301      case CHAP_CHALLENGE:
302	RecvChapTalk(bundle, chp, bp, physical);
303	break;
304      case CHAP_SUCCESS:
305      case CHAP_FAILURE:
306	RecvChapResult(bundle, chp, bp, physical);
307	break;
308      }
309    }
310  }
311  mbuf_Free(bp);
312}
313