chap.c revision 43693
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.39 1999/01/29 22:46:31 brian Exp $
21 *
22 *	TODO:
23 */
24#include <sys/param.h>
25#include <netinet/in.h>
26#include <netinet/in_systm.h>
27#include <netinet/ip.h>
28#include <sys/un.h>
29
30#ifdef HAVE_DES
31#include <md4.h>
32#include <string.h>
33#endif
34#include <md5.h>
35#include <stdlib.h>
36#include <termios.h>
37
38#include "mbuf.h"
39#include "log.h"
40#include "defs.h"
41#include "timer.h"
42#include "fsm.h"
43#include "lcpproto.h"
44#include "lcp.h"
45#include "lqr.h"
46#include "hdlc.h"
47#include "auth.h"
48#include "chap.h"
49#include "async.h"
50#include "throughput.h"
51#include "descriptor.h"
52#include "iplist.h"
53#include "slcompress.h"
54#include "ipcp.h"
55#include "filter.h"
56#include "ccp.h"
57#include "link.h"
58#include "physical.h"
59#include "mp.h"
60#ifndef NORADIUS
61#include "radius.h"
62#endif
63#include "bundle.h"
64#include "chat.h"
65#include "cbcp.h"
66#include "datalink.h"
67#ifdef HAVE_DES
68#include "chap_ms.h"
69#endif
70
71static const char *chapcodes[] = {
72  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
73};
74#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1)
75
76static void
77ChapOutput(struct physical *physical, u_int code, u_int id,
78	   const u_char *ptr, int count, const char *text)
79{
80  int plen;
81  struct fsmheader lh;
82  struct mbuf *bp;
83
84  plen = sizeof(struct fsmheader) + count;
85  lh.code = code;
86  lh.id = id;
87  lh.length = htons(plen);
88  bp = mbuf_Alloc(plen, MB_FSM);
89  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
90  if (count)
91    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
92  log_DumpBp(LogDEBUG, "ChapOutput", bp);
93  if (text == NULL)
94    log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]);
95  else
96    log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text);
97  hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp);
98}
99
100static char *
101chap_BuildAnswer(char *name, char *key, u_char id, char *challenge, int MSChap)
102{
103  char *result, *digest;
104  size_t nlen, klen;
105
106  nlen = strlen(name);
107  klen = strlen(key);
108
109#ifdef HAVE_DES
110  if (MSChap) {
111    char expkey[AUTHLEN << 2];
112    MD4_CTX MD4context;
113    int f;
114
115    if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL)
116      return result;
117
118    digest = result;				/* this is the response */
119    *digest++ = MS_CHAP_RESPONSE_LEN;		/* 49 */
120    memset(digest, '\0', 24);
121    digest += 24;
122
123    for (f = klen; f; f--) {
124      expkey[2*f-2] = key[f-1];
125      expkey[2*f-1] = 0;
126    }
127
128    /*
129     *           -----------
130     * answer = | k\0e\0y\0 |
131     *           -----------
132     */
133    MD4Init(&MD4context);
134    MD4Update(&MD4context, expkey, klen << 1);
135    MD4Final(digest, &MD4context);
136    memcpy(digest + 25, name, nlen);
137
138    /*
139     * ``result'' is:
140     *           ---- --------- -------------------- ------
141     * result = | 49 | 24 * \0 | digest (pad to 25) | name |
142     *           ---- --------- -------------------- ------
143     */
144    chap_MS(digest, challenge + 1, *challenge);
145
146    /*
147     *           ---- --------- ---------------- --- ----------
148     * result = | 49 | 24 * \0 | 24 byte digest | 1 | authname |
149     *           ---- --------- ---------------- --- ----------
150     */
151  } else
152#endif
153  if ((result = malloc(nlen + 17)) != NULL) {
154    /* Normal MD5 stuff */
155    MD5_CTX MD5context;
156
157    digest = result;
158    *digest++ = 16;				/* value size */
159
160    MD5Init(&MD5context);
161log_Printf(LogPHASE, "Build with 0x%x, %s & %.*s\n", id, key, *challenge, challenge+1);
162    MD5Update(&MD5context, &id, 1);
163    MD5Update(&MD5context, key, klen);
164    MD5Update(&MD5context, challenge + 1, *challenge);
165    MD5Final(digest, &MD5context);
166
167    memcpy(digest + 16, name, nlen);
168    /*
169     *           ---- -------- ------
170     * result = | 16 | digest | name |
171     *           ---- -------- ------
172     */
173  }
174
175  return result;
176}
177
178static void
179chap_Challenge(struct authinfo *authp)
180{
181  struct chap *chap = auth2chap(authp);
182  int len, i;
183  char *cp;
184
185  randinit();
186  cp = chap->challenge;
187
188#ifndef NORADIUS
189  if (*authp->physical->dl->bundle->radius.cfg.file) {
190    /* For radius, our challenge is 16 readable NUL terminated bytes :*/
191    *cp++ = 16;
192    for (i = 0; i < 16; i++)
193      *cp++ = (random() % 10) + '0';
194  } else
195#endif
196  {
197    *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
198    for (i = 0; i < *chap->challenge; i++)
199      *cp++ = random() & 0xff;
200  }
201
202  len = strlen(authp->physical->dl->bundle->cfg.auth.name);
203  memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
204  cp += len;
205  ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, chap->challenge,
206	     cp - chap->challenge, NULL);
207}
208
209static void
210chap_Success(struct authinfo *authp)
211{
212  datalink_GotAuthname(authp->physical->dl, authp->in.name);
213  ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, "Welcome!!", 10, NULL);
214  authp->physical->link.lcp.auth_ineed = 0;
215  if (Enabled(authp->physical->dl->bundle, OPT_UTMP))
216    physical_Login(authp->physical, authp->in.name);
217
218  if (authp->physical->link.lcp.auth_iwait == 0)
219    /*
220     * Either I didn't need to authenticate, or I've already been
221     * told that I got the answer right.
222     */
223    datalink_AuthOk(authp->physical->dl);
224}
225
226static void
227chap_Failure(struct authinfo *authp)
228{
229  ChapOutput(authp->physical, CHAP_FAILURE, authp->id, "Invalid!!", 9, NULL);
230  datalink_AuthNotOk(authp->physical->dl);
231}
232
233void
234chap_Init(struct chap *chap, struct physical *p)
235{
236  auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure);
237  *chap->challenge = 0;
238  chap->using_MSChap = 0;
239}
240
241void
242chap_Input(struct physical *p, struct mbuf *bp)
243{
244  struct chap *chap = &p->dl->chap;
245  char *name, *key, *ans, *myans;
246  int len, nlen;
247  u_char alen;
248
249  if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL)
250    log_Printf(LogERROR, "Chap Input: Truncated header !\n");
251  else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE)
252    log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n",
253               chap->auth.in.hdr.code);
254  else {
255    len = mbuf_Length(bp);
256    ans = NULL;
257
258    if (chap->auth.in.hdr.code != CHAP_CHALLENGE &&
259        chap->auth.id != chap->auth.in.hdr.id &&
260        Enabled(p->dl->bundle, OPT_IDCHECK)) {
261      /* Wrong conversation dude ! */
262      log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n",
263                 chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id,
264                 chap->auth.id);
265      mbuf_Free(bp);
266      return;
267    }
268    chap->auth.id = chap->auth.in.hdr.id;	/* We respond with this id */
269
270    switch (chap->auth.in.hdr.code) {
271      case CHAP_CHALLENGE:
272        bp = mbuf_Read(bp, chap->challenge, 1);
273        len -= *chap->challenge + 1;
274        if (len < 0) {
275          log_Printf(LogERROR, "Chap Input: Truncated challenge !\n");
276          mbuf_Free(bp);
277          return;
278        }
279        bp = mbuf_Read(bp, chap->challenge + 1, *chap->challenge);
280        bp = auth_ReadName(&chap->auth, bp, len);
281        break;
282
283      case CHAP_RESPONSE:
284        auth_StopTimer(&chap->auth);
285        bp = mbuf_Read(bp, &alen, 1);
286        len -= alen + 1;
287        if (len < 0) {
288          log_Printf(LogERROR, "Chap Input: Truncated response !\n");
289          mbuf_Free(bp);
290          return;
291        }
292        if ((ans = malloc(alen + 2)) == NULL) {
293          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
294          mbuf_Free(bp);
295          return;
296        }
297        *ans = chap->auth.id;
298        bp = mbuf_Read(bp, ans + 1, alen);
299        ans[alen+1] = '\0';
300        bp = auth_ReadName(&chap->auth, bp, len);
301        break;
302
303      case CHAP_SUCCESS:
304      case CHAP_FAILURE:
305        /* chap->auth.in.name is already set up at CHALLENGE time */
306        if ((ans = malloc(len + 1)) == NULL) {
307          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
308          mbuf_Free(bp);
309          return;
310        }
311        bp = mbuf_Read(bp, ans, len);
312        ans[len] = '\0';
313        break;
314    }
315
316    switch (chap->auth.in.hdr.code) {
317      case CHAP_CHALLENGE:
318      case CHAP_RESPONSE:
319        if (*chap->auth.in.name)
320          log_Printf(LogPHASE, "Chap Input: %s (from %s)\n",
321                     chapcodes[chap->auth.in.hdr.code], chap->auth.in.name);
322        else
323          log_Printf(LogPHASE, "Chap Input: %s\n",
324                     chapcodes[chap->auth.in.hdr.code]);
325        break;
326
327      case CHAP_SUCCESS:
328      case CHAP_FAILURE:
329        if (*ans)
330          log_Printf(LogPHASE, "Chap Input: %s (%s)\n",
331                     chapcodes[chap->auth.in.hdr.code], ans);
332        else
333          log_Printf(LogPHASE, "Chap Input: %s\n",
334                     chapcodes[chap->auth.in.hdr.code]);
335        break;
336    }
337
338    switch (chap->auth.in.hdr.code) {
339      case CHAP_CHALLENGE:
340        name = p->dl->bundle->cfg.auth.name;
341        nlen = strlen(name);
342        key = p->dl->bundle->cfg.auth.key;
343        myans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge, 0);
344        if (myans) {
345          ChapOutput(p, CHAP_RESPONSE, chap->auth.id, myans,
346                     *myans + 1 + nlen, name);
347          free(myans);
348        } else
349          ChapOutput(p, CHAP_FAILURE, chap->auth.id, "Out of memory!",
350                     14, NULL);
351        break;
352
353      case CHAP_RESPONSE:
354        name = chap->auth.in.name;
355        nlen = strlen(name);
356#ifndef NORADIUS
357        if (*p->dl->bundle->radius.cfg.file) {
358          chap->challenge[*chap->challenge+1] = '\0';
359log_Printf(LogPHASE, "Challenge %s, answer is %d bytes starting with %d\n", chap->challenge+1, alen+1, *ans);
360          radius_Authenticate(&p->dl->bundle->radius, &chap->auth,
361                              chap->auth.in.name, ans, chap->challenge + 1);
362        } else
363#endif
364        {
365          key = auth_GetSecret(p->dl->bundle, name, nlen, p);
366          if (key) {
367            myans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge,
368                                     chap->using_MSChap);
369            if (myans == NULL)
370              key = NULL;
371            else {
372              if (memcmp(myans, ans, 1 + *myans))
373                key = NULL;
374              free(myans);
375            }
376          }
377
378          if (key)
379            chap_Success(&chap->auth);
380          else
381            chap_Failure(&chap->auth);
382        }
383
384        break;
385
386      case CHAP_SUCCESS:
387        if (p->link.lcp.auth_iwait == PROTO_CHAP) {
388          p->link.lcp.auth_iwait = 0;
389          if (p->link.lcp.auth_ineed == 0)
390            /*
391             * We've succeeded in our ``login''
392             * If we're not expecting  the peer to authenticate (or he already
393             * has), proceed to network phase.
394             */
395            datalink_AuthOk(p->dl);
396        }
397        break;
398
399      case CHAP_FAILURE:
400        datalink_AuthNotOk(p->dl);
401        break;
402    }
403    free(ans);
404  }
405
406  mbuf_Free(bp);
407}
408