chap.c revision 38559
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
68static const char *chapcodes[] = {
69  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
70};
71
72static void
73ChapOutput(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
96void
97chap_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
115static void
116RecvChapTalk(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
265static void
266RecvChapResult(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
291void
292chap_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