chap.c revision 31051
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.24 1997/10/26 01:02:16 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 "mbuf.h"
45#include "log.h"
46#include "defs.h"
47#include "timer.h"
48#include "fsm.h"
49#include "chap.h"
50#include "chap_ms.h"
51#include "lcpproto.h"
52#include "lcp.h"
53#include "hdlc.h"
54#include "phase.h"
55#include "loadalias.h"
56#include "command.h"
57#include "vars.h"
58#include "auth.h"
59
60static char *chapcodes[] = {
61  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
62};
63
64static void
65ChapOutput(u_int code, u_int id, u_char * ptr, int count)
66{
67  int plen;
68  struct fsmheader lh;
69  struct mbuf *bp;
70
71  plen = sizeof(struct fsmheader) + count;
72  lh.code = code;
73  lh.id = id;
74  lh.length = htons(plen);
75  bp = mballoc(plen, MB_FSM);
76  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
77  if (count)
78    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
79  LogDumpBp(LogDEBUG, "ChapOutput", bp);
80  LogPrintf(LogLCP, "ChapOutput: %s\n", chapcodes[code]);
81  HdlcOutput(PRI_LINK, PROTO_CHAP, bp);
82}
83
84
85static char challenge_data[80];
86static int challenge_len;
87
88static void
89SendChapChallenge(int chapid)
90{
91  int len, i;
92  char *cp;
93
94  randinit();
95  cp = challenge_data;
96  *cp++ = challenge_len = random() % 32 + 16;
97  for (i = 0; i < challenge_len; i++)
98    *cp++ = random() & 0xff;
99  len = strlen(VarAuthName);
100  memcpy(cp, VarAuthName, len);
101  cp += len;
102  ChapOutput(CHAP_CHALLENGE, chapid, challenge_data, cp - challenge_data);
103}
104
105struct authinfo AuthChapInfo = {
106  SendChapChallenge,
107};
108
109static void
110RecvChapTalk(struct fsmheader *chp, struct mbuf *bp)
111{
112  int valsize, len;
113  int arglen, keylen, namelen;
114  char *cp, *argp, *ap, *name, *digest;
115  char *keyp;
116  MD5_CTX MD5context;		/* context for MD5 */
117  char answer[100];
118  char cdigest[16];
119#ifdef HAVE_DES
120  int ix;
121  MD4_CTX MD4context;		/* context for MD4 */
122#endif
123
124  len = ntohs(chp->length);
125  LogPrintf(LogDEBUG, "RecvChapTalk: length: %d\n", len);
126  arglen = len - sizeof(struct fsmheader);
127  cp = (char *) MBUF_CTOP(bp);
128  valsize = *cp++ & 255;
129  name = cp + valsize;
130  namelen = arglen - valsize - 1;
131  name[namelen] = 0;
132  LogPrintf(LogPHASE, " Valsize = %d, Name = %s\n", valsize, name);
133
134  switch (chp->code) {
135  case CHAP_CHALLENGE:
136    keyp = VarAuthKey;
137    keylen = strlen(VarAuthKey);
138    name = VarAuthName;
139    namelen = strlen(VarAuthName);
140
141#ifdef HAVE_DES
142    if (VarMSChap)
143      argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN);
144    else
145#endif
146      argp = malloc(1 + valsize + namelen + 16);
147
148    if (argp == NULL) {
149      ChapOutput(CHAP_FAILURE, chp->id, "Out of memory!", 14);
150      return;
151    }
152#ifdef HAVE_DES
153    if (VarMSChap) {
154      digest = argp;     /* this is the response */
155      *digest++ = MS_CHAP_RESPONSE_LEN;   /* 49 */
156      memset(digest, '\0', 24);
157      digest += 24;
158
159      ap = answer;       /* this is the challenge */
160      memcpy(ap, keyp, keylen);
161      ap += 2 * keylen;
162      memcpy(ap, cp, valsize);
163      LogDumpBuff(LogDEBUG, "recv", ap, valsize);
164      ap += valsize;
165      for (ix = keylen; ix > 0 ; ix--) {
166          answer[2*ix-2] = answer[ix-1];
167          answer[2*ix-1] = 0;
168      }
169      MD4Init(&MD4context);
170      MD4Update(&MD4context, answer, 2 * keylen);
171      MD4Final(digest, &MD4context);
172      memcpy(digest + 25, name, namelen);
173      ap += 2 * keylen;
174      ChapMS(digest, answer + 2 * keylen, valsize);
175      LogDumpBuff(LogDEBUG, "answer", digest, 24);
176      ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + MS_CHAP_RESPONSE_LEN + 1);
177    } else {
178#endif
179      digest = argp;
180      *digest++ = 16;		/* value size */
181      ap = answer;
182      *ap++ = chp->id;
183      memcpy(ap, keyp, keylen);
184      ap += keylen;
185      memcpy(ap, cp, valsize);
186      LogDumpBuff(LogDEBUG, "recv", ap, valsize);
187      ap += valsize;
188      MD5Init(&MD5context);
189      MD5Update(&MD5context, answer, ap - answer);
190      MD5Final(digest, &MD5context);
191      LogDumpBuff(LogDEBUG, "answer", digest, 16);
192      memcpy(digest + 16, name, namelen);
193      ap += namelen;
194      /* Send answer to the peer */
195      ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + 17);
196#ifdef HAVE_DES
197    }
198#endif
199    free(argp);
200    break;
201  case CHAP_RESPONSE:
202    /*
203     * Get a secret key corresponds to the peer
204     */
205    keyp = AuthGetSecret(SECRETFILE, name, namelen, chp->code == CHAP_RESPONSE);
206    if (keyp) {
207      /*
208       * Compute correct digest value
209       */
210      keylen = strlen(keyp);
211      ap = answer;
212      *ap++ = chp->id;
213      memcpy(ap, keyp, keylen);
214      ap += keylen;
215      MD5Init(&MD5context);
216      MD5Update(&MD5context, answer, ap - answer);
217      MD5Update(&MD5context, challenge_data + 1, challenge_len);
218      MD5Final(cdigest, &MD5context);
219      LogDumpBuff(LogDEBUG, "got", cp, 16);
220      LogDumpBuff(LogDEBUG, "expect", cdigest, 16);
221
222      /*
223       * Compare with the response
224       */
225      if (memcmp(cp, cdigest, 16) == 0) {
226	ChapOutput(CHAP_SUCCESS, chp->id, "Welcome!!", 10);
227        if ((mode & MODE_DIRECT) && isatty(modem) && Enabled(ConfUtmp))
228	  if (Utmp)
229	    LogPrintf(LogERROR, "Oops, already logged in on %s\n",
230		      VarBaseDevice);
231	  else {
232	    struct utmp ut;
233	    memset(&ut, 0, sizeof(ut));
234	    time(&ut.ut_time);
235	    strncpy(ut.ut_name, name, sizeof(ut.ut_name)-1);
236	    strncpy(ut.ut_line, VarBaseDevice, sizeof(ut.ut_line)-1);
237	    if (logout(ut.ut_line))
238	      logwtmp(ut.ut_line, "", "");
239	    login(&ut);
240	    Utmp = 1;
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