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