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