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