chap.c revision 43313
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.37 1998/08/26 18:07:56 brian Exp $
21 *
22 *	TODO:
23 */
24#include <sys/param.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#ifndef NORADIUS
61#include "radius.h"
62#endif
63#include "bundle.h"
64#include "chat.h"
65#include "cbcp.h"
66#include "datalink.h"
67#ifdef HAVE_DES
68#include "chap_ms.h"
69#endif
70
71static const char *chapcodes[] = {
72  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
73};
74
75static void
76ChapOutput(struct physical *physical, u_int code, u_int id,
77	   const u_char * ptr, int count, const char *text)
78{
79  int plen;
80  struct fsmheader lh;
81  struct mbuf *bp;
82
83  plen = sizeof(struct fsmheader) + count;
84  lh.code = code;
85  lh.id = id;
86  lh.length = htons(plen);
87  bp = mbuf_Alloc(plen, MB_FSM);
88  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
89  if (count)
90    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
91  log_DumpBp(LogDEBUG, "ChapOutput", bp);
92  if (text == NULL)
93    log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]);
94  else
95    log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text);
96  hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp);
97}
98
99void
100chap_SendChallenge(struct authinfo *auth, int chapid, struct physical *physical)
101{
102  struct chap *chap = auth2chap(auth);
103  int len, i;
104  char *cp;
105
106  randinit();
107  cp = chap->challenge_data;
108#ifndef NORADIUS
109  if (*physical->dl->bundle->radius.cfg.file) {
110    /* For radius, our challenge is 16 readable NUL terminated bytes :*/
111    *cp++ = chap->challenge_len = 16;
112    for (i = 0; i < chap->challenge_len; i++)
113      *cp++ = (random() & (0x7f - 0x20)) + 0x20;
114    *cp = '\0';
115  } else {
116#endif
117    *cp++ = chap->challenge_len = random() % (CHAPCHALLENGELEN-16) + 16;
118    for (i = 0; i < chap->challenge_len; i++)
119      *cp++ = random() & 0xff;
120    len = strlen(physical->dl->bundle->cfg.auth.name);
121    memcpy(cp, physical->dl->bundle->cfg.auth.name, len);
122    cp += len;
123#ifndef NORADIUS
124  }
125#endif
126  ChapOutput(physical, CHAP_CHALLENGE, chapid, chap->challenge_data,
127	     cp - chap->challenge_data, NULL);
128}
129
130static void
131RecvChapTalk(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
132             struct physical *physical)
133{
134  int valsize, len;
135  int arglen, keylen, namelen;
136  char *cp, *argp, *ap, *name, *digest;
137  char *keyp;
138  MD5_CTX MD5context;		/* context for MD5 */
139  char answer[CHAPDIGESTLEN];
140  char cdigest[16];
141#ifdef HAVE_DES
142  int ix;
143  MD4_CTX MD4context;		/* context for MD4 */
144#endif
145
146  len = ntohs(chp->length);
147  log_Printf(LogDEBUG, "RecvChapTalk: length: %d\n", len);
148  arglen = len - sizeof(struct fsmheader);
149  cp = (char *) MBUF_CTOP(bp);
150  valsize = *cp++ & 255;
151  name = cp + valsize;
152  namelen = arglen - valsize - 1;
153  name[namelen] = 0;
154
155  log_Printf(LogPHASE, "Chap Input: %s (from %s)\n",
156             chapcodes[chp->code], name);
157
158  switch (chp->code) {
159  case CHAP_CHALLENGE:
160    keyp = bundle->cfg.auth.key;
161    keylen = strlen(bundle->cfg.auth.key);
162    name = bundle->cfg.auth.name;
163    namelen = strlen(bundle->cfg.auth.name);
164
165#ifdef HAVE_DES
166    if (physical->dl->chap.using_MSChap)
167      argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN);
168    else
169#endif
170      argp = malloc(1 + valsize + namelen + 16);
171
172    if (argp == NULL) {
173      ChapOutput(physical, CHAP_FAILURE, chp->id, "Out of memory!", 14, NULL);
174      return;
175    }
176#ifdef HAVE_DES
177    if (physical->dl->chap.using_MSChap) {
178      digest = argp;     /* this is the response */
179      *digest++ = MS_CHAP_RESPONSE_LEN;   /* 49 */
180      memset(digest, '\0', 24);
181      digest += 24;
182
183      ap = answer;       /* this is the challenge */
184      memcpy(ap, keyp, keylen);
185      ap += 2 * keylen;
186      memcpy(ap, cp, valsize);
187      log_DumpBuff(LogDEBUG, "recv", ap, valsize);
188      ap += valsize;
189      for (ix = keylen; ix > 0 ; ix--) {
190          answer[2*ix-2] = answer[ix-1];
191          answer[2*ix-1] = 0;
192      }
193      MD4Init(&MD4context);
194      MD4Update(&MD4context, answer, 2 * keylen);
195      MD4Final(digest, &MD4context);
196      memcpy(digest + 25, name, namelen);
197      ap += 2 * keylen;
198      chap_MS(digest, answer + 2 * keylen, valsize);
199      log_DumpBuff(LogDEBUG, "answer", digest, 24);
200      ChapOutput(physical, CHAP_RESPONSE, chp->id, argp,
201		 namelen + MS_CHAP_RESPONSE_LEN + 1, name);
202    } else {
203#endif
204      digest = argp;
205      *digest++ = 16;		/* value size */
206      ap = answer;
207      *ap++ = chp->id;
208      memcpy(ap, keyp, keylen);
209      ap += keylen;
210      memcpy(ap, cp, valsize);
211      log_DumpBuff(LogDEBUG, "recv", ap, valsize);
212      ap += valsize;
213      MD5Init(&MD5context);
214      MD5Update(&MD5context, answer, ap - answer);
215      MD5Final(digest, &MD5context);
216      log_DumpBuff(LogDEBUG, "answer", digest, 16);
217      memcpy(digest + 16, name, namelen);
218      ap += namelen;
219      /* Send answer to the peer */
220      ChapOutput(physical, CHAP_RESPONSE, chp->id, argp, namelen + 17, name);
221#ifdef HAVE_DES
222    }
223#endif
224    free(argp);
225    if (*name == '\0')
226      log_Printf(LogWARN, "Sending empty CHAP authname!\n");
227    break;
228  case CHAP_RESPONSE:
229    /*
230     * Get a secret key corresponds to the peer
231     */
232#ifndef NORADIUS
233    if (*bundle->radius.cfg.file) {
234      char chapname[AUTHLEN];
235
236      if (namelen > AUTHLEN - 1)
237        namelen = AUTHLEN - 1;
238      strncpy(chapname, name, namelen);
239      chapname[namelen] = '\0';
240      strncpy(answer, cp-1, 17);
241      answer[17] = '\0';
242
243      if (radius_Authenticate(&bundle->radius, bundle, chapname, answer,
244                              physical->dl->chap.challenge_data + 1))
245        break;		/* And there was much rejoicing ! */
246
247    } else
248#endif
249    if ((keyp = auth_GetSecret(bundle, name, namelen, physical))) {
250      /* Compute correct digest value */
251      keylen = strlen(keyp);
252      ap = answer;
253      *ap++ = chp->id;
254      memcpy(ap, keyp, keylen);
255      ap += keylen;
256      MD5Init(&MD5context);
257      MD5Update(&MD5context, answer, ap - answer);
258      MD5Update(&MD5context, physical->dl->chap.challenge_data + 1,
259                physical->dl->chap.challenge_len);
260      MD5Final(cdigest, &MD5context);
261      log_DumpBuff(LogDEBUG, "got", cp, 16);
262      log_DumpBuff(LogDEBUG, "expect", cdigest, 16);
263
264      /*
265       * Compare with the response
266       */
267      if (memcmp(cp, cdigest, 16) == 0) {
268        datalink_GotAuthname(physical->dl, name, namelen);
269	ChapOutput(physical, CHAP_SUCCESS, chp->id, "Welcome!!", 10, NULL);
270	physical->link.lcp.auth_ineed = 0;
271        if (Enabled(bundle, OPT_UTMP))
272          physical_Login(physical, name);
273
274        if (physical->link.lcp.auth_iwait == 0)
275          /*
276           * Either I didn't need to authenticate, or I've already been
277           * told that I got the answer right.
278           */
279          datalink_AuthOk(physical->dl);
280
281	break;
282      }
283    }
284
285    /*
286     * Peer is not registerd, or response digest is wrong.
287     */
288    ChapOutput(physical, CHAP_FAILURE, chp->id, "Invalid!!", 9, NULL);
289    datalink_AuthNotOk(physical->dl);
290    break;
291  }
292}
293
294static void
295RecvChapResult(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
296	       struct physical *physical)
297{
298  int len;
299
300  len = ntohs(chp->length);
301  log_Printf(LogDEBUG, "RecvChapResult: length: %d\n", len);
302  if (chp->code == CHAP_SUCCESS) {
303    if (physical->link.lcp.auth_iwait == PROTO_CHAP) {
304      physical->link.lcp.auth_iwait = 0;
305      if (physical->link.lcp.auth_ineed == 0)
306        /*
307         * We've succeeded in our ``login''
308         * If we're not expecting  the peer to authenticate (or he already
309         * has), proceed to network phase.
310         */
311        datalink_AuthOk(physical->dl);
312    }
313  } else {
314    /* CHAP failed - it's not going to get any better */
315    log_Printf(LogPHASE, "Chap Input: Giving up after name/key FAILURE\n");
316    datalink_AuthNotOk(physical->dl);
317  }
318}
319
320void
321chap_Input(struct bundle *bundle, struct mbuf *bp, struct physical *physical)
322{
323  int len = mbuf_Length(bp);
324  struct fsmheader *chp;
325
326  if (len >= sizeof(struct fsmheader)) {
327    chp = (struct fsmheader *) MBUF_CTOP(bp);
328    if (len >= ntohs(chp->length)) {
329      if (chp->code < 1 || chp->code > 4)
330	chp->code = 0;
331      bp->offset += sizeof(struct fsmheader);
332      bp->cnt -= sizeof(struct fsmheader);
333
334      switch (chp->code) {
335      case CHAP_RESPONSE:
336	auth_StopTimer(&physical->dl->chap.auth);
337	/* Fall into.. */
338      case CHAP_CHALLENGE:
339	RecvChapTalk(bundle, chp, bp, physical);
340	break;
341      case CHAP_SUCCESS:
342      case CHAP_FAILURE:
343        log_Printf(LogPHASE, "Chap Input: %s\n", chapcodes[chp->code]);
344	RecvChapResult(bundle, chp, bp, physical);
345	break;
346      }
347    }
348  }
349  mbuf_Free(bp);
350}
351