chap.c revision 30715
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.23 1997/09/25 00:52:32 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  /*
135   * Get a secret key corresponds to the peer
136   */
137  keyp = AuthGetSecret(SECRETFILE, name, namelen, chp->code == CHAP_RESPONSE);
138
139  switch (chp->code) {
140  case CHAP_CHALLENGE:
141    if (keyp) {
142      keylen = strlen(keyp);
143    } else {
144      keylen = strlen(VarAuthKey);
145      keyp = VarAuthKey;
146    }
147    name = VarAuthName;
148    namelen = strlen(VarAuthName);
149
150#ifdef HAVE_DES
151    if (VarMSChap)
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(CHAP_FAILURE, chp->id, "Out of memory!", 14);
159      return;
160    }
161#ifdef HAVE_DES
162    if (VarMSChap) {
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      LogDumpBuff(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      ChapMS(digest, answer + 2 * keylen, valsize);
184      LogDumpBuff(LogDEBUG, "answer", digest, 24);
185      ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + MS_CHAP_RESPONSE_LEN + 1);
186    } else {
187#endif
188      digest = argp;
189      *digest++ = 16;		/* value size */
190      ap = answer;
191      *ap++ = chp->id;
192      memcpy(ap, keyp, keylen);
193      ap += keylen;
194      memcpy(ap, cp, valsize);
195      LogDumpBuff(LogDEBUG, "recv", ap, valsize);
196      ap += valsize;
197      MD5Init(&MD5context);
198      MD5Update(&MD5context, answer, ap - answer);
199      MD5Final(digest, &MD5context);
200      LogDumpBuff(LogDEBUG, "answer", digest, 16);
201      memcpy(digest + 16, name, namelen);
202      ap += namelen;
203      /* Send answer to the peer */
204      ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + 17);
205#ifdef HAVE_DES
206    }
207#endif
208    free(argp);
209    break;
210  case CHAP_RESPONSE:
211    if (keyp) {
212
213      /*
214       * Compute correct digest value
215       */
216      keylen = strlen(keyp);
217      ap = answer;
218      *ap++ = chp->id;
219      memcpy(ap, keyp, keylen);
220      ap += keylen;
221      MD5Init(&MD5context);
222      MD5Update(&MD5context, answer, ap - answer);
223      MD5Update(&MD5context, challenge_data + 1, challenge_len);
224      MD5Final(cdigest, &MD5context);
225      LogDumpBuff(LogDEBUG, "got", cp, 16);
226      LogDumpBuff(LogDEBUG, "expect", cdigest, 16);
227
228      /*
229       * Compare with the response
230       */
231      if (memcmp(cp, cdigest, 16) == 0) {
232	ChapOutput(CHAP_SUCCESS, chp->id, "Welcome!!", 10);
233        if ((mode & MODE_DIRECT) && isatty(modem) && Enabled(ConfUtmp))
234	  if (Utmp)
235	    LogPrintf(LogERROR, "Oops, already logged in on %s\n",
236		      VarBaseDevice);
237	  else {
238	    struct utmp ut;
239	    memset(&ut, 0, sizeof(ut));
240	    time(&ut.ut_time);
241	    strncpy(ut.ut_name, name, sizeof(ut.ut_name)-1);
242	    strncpy(ut.ut_line, VarBaseDevice, sizeof(ut.ut_line)-1);
243	    if (logout(ut.ut_line))
244	      logwtmp(ut.ut_line, "", "");
245	    login(&ut);
246	    Utmp = 1;
247	  }
248	NewPhase(PHASE_NETWORK);
249	break;
250      }
251    }
252
253    /*
254     * Peer is not registerd, or response digest is wrong.
255     */
256    ChapOutput(CHAP_FAILURE, chp->id, "Invalid!!", 9);
257    reconnect(RECON_FALSE);
258    LcpClose();
259    break;
260  }
261}
262
263static void
264RecvChapResult(struct fsmheader *chp, struct mbuf *bp)
265{
266  int len;
267  struct lcpstate *lcp = &LcpInfo;
268
269  len = ntohs(chp->length);
270  LogPrintf(LogDEBUG, "RecvChapResult: length: %d\n", len);
271  if (chp->code == CHAP_SUCCESS) {
272    if (lcp->auth_iwait == PROTO_CHAP) {
273      lcp->auth_iwait = 0;
274      if (lcp->auth_ineed == 0)
275	NewPhase(PHASE_NETWORK);
276    }
277  } else {
278
279    /*
280     * Maybe, we shoud close LCP. Of cause, peer may take close action, too.
281     */
282    ;
283  }
284}
285
286void
287ChapInput(struct mbuf *bp)
288{
289  int len = plength(bp);
290  struct fsmheader *chp;
291
292  if (len >= sizeof(struct fsmheader)) {
293    chp = (struct fsmheader *) MBUF_CTOP(bp);
294    if (len >= ntohs(chp->length)) {
295      if (chp->code < 1 || chp->code > 4)
296	chp->code = 0;
297      LogPrintf(LogLCP, "ChapInput: %s\n", chapcodes[chp->code]);
298
299      bp->offset += sizeof(struct fsmheader);
300      bp->cnt -= sizeof(struct fsmheader);
301
302      switch (chp->code) {
303      case CHAP_RESPONSE:
304	StopAuthTimer(&AuthChapInfo);
305	/* Fall into.. */
306      case CHAP_CHALLENGE:
307	RecvChapTalk(chp, bp);
308	break;
309      case CHAP_SUCCESS:
310      case CHAP_FAILURE:
311	RecvChapResult(chp, bp);
312	break;
313      }
314    }
315  }
316  pfree(bp);
317}
318