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