chap.c revision 43743
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.41 1999/02/07 13:48:38 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);
161    MD5Update(&MD5context, &id, 1);
162    MD5Update(&MD5context, key, klen);
163    MD5Update(&MD5context, challenge + 1, *challenge);
164    MD5Final(digest, &MD5context);
165
166    memcpy(digest + 16, name, nlen);
167    /*
168     *           ---- -------- ------
169     * result = | 16 | digest | name |
170     *           ---- -------- ------
171     */
172  }
173
174  return result;
175}
176
177static void
178chap_Challenge(struct authinfo *authp)
179{
180  struct chap *chap = auth2chap(authp);
181  int len, i;
182  char *cp;
183
184  randinit();
185  cp = chap->challenge;
186
187#ifndef NORADIUS
188  if (*authp->physical->dl->bundle->radius.cfg.file) {
189    /* For radius, our challenge is 16 readable NUL terminated bytes :*/
190    *cp++ = 16;
191    for (i = 0; i < 16; i++)
192      *cp++ = (random() % 10) + '0';
193  } else
194#endif
195  {
196    *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
197    for (i = 0; i < *chap->challenge; i++)
198      *cp++ = random() & 0xff;
199  }
200
201  len = strlen(authp->physical->dl->bundle->cfg.auth.name);
202  memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
203  cp += len;
204  ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, chap->challenge,
205	     cp - chap->challenge, NULL);
206}
207
208static void
209chap_Success(struct authinfo *authp)
210{
211  datalink_GotAuthname(authp->physical->dl, authp->in.name);
212  ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, "Welcome!!", 10, NULL);
213  authp->physical->link.lcp.auth_ineed = 0;
214  if (Enabled(authp->physical->dl->bundle, OPT_UTMP))
215    physical_Login(authp->physical, authp->in.name);
216
217  if (authp->physical->link.lcp.auth_iwait == 0)
218    /*
219     * Either I didn't need to authenticate, or I've already been
220     * told that I got the answer right.
221     */
222    datalink_AuthOk(authp->physical->dl);
223}
224
225static void
226chap_Failure(struct authinfo *authp)
227{
228  ChapOutput(authp->physical, CHAP_FAILURE, authp->id, "Invalid!!", 9, NULL);
229  datalink_AuthNotOk(authp->physical->dl);
230}
231
232void
233chap_Init(struct chap *chap, struct physical *p)
234{
235  auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure);
236  *chap->challenge = 0;
237  chap->using_MSChap = 0;
238}
239
240void
241chap_Input(struct physical *p, struct mbuf *bp)
242{
243  struct chap *chap = &p->dl->chap;
244  char *name, *key, *ans, *myans;
245  int len, nlen;
246  u_char alen;
247
248  if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL)
249    log_Printf(LogERROR, "Chap Input: Truncated header !\n");
250  else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE)
251    log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n",
252               chap->auth.in.hdr.code);
253  else {
254    len = mbuf_Length(bp);
255    ans = NULL;
256
257    if (chap->auth.in.hdr.code != CHAP_CHALLENGE &&
258        chap->auth.id != chap->auth.in.hdr.id &&
259        Enabled(p->dl->bundle, OPT_IDCHECK)) {
260      /* Wrong conversation dude ! */
261      log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n",
262                 chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id,
263                 chap->auth.id);
264      mbuf_Free(bp);
265      return;
266    }
267    chap->auth.id = chap->auth.in.hdr.id;	/* We respond with this id */
268
269    switch (chap->auth.in.hdr.code) {
270      case CHAP_CHALLENGE:
271        bp = mbuf_Read(bp, chap->challenge, 1);
272        len -= *chap->challenge + 1;
273        if (len < 0) {
274          log_Printf(LogERROR, "Chap Input: Truncated challenge !\n");
275          mbuf_Free(bp);
276          return;
277        }
278        bp = mbuf_Read(bp, chap->challenge + 1, *chap->challenge);
279        bp = auth_ReadName(&chap->auth, bp, len);
280        break;
281
282      case CHAP_RESPONSE:
283        auth_StopTimer(&chap->auth);
284        bp = mbuf_Read(bp, &alen, 1);
285        len -= alen + 1;
286        if (len < 0) {
287          log_Printf(LogERROR, "Chap Input: Truncated response !\n");
288          mbuf_Free(bp);
289          return;
290        }
291        if ((ans = malloc(alen + 2)) == NULL) {
292          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
293          mbuf_Free(bp);
294          return;
295        }
296        *ans = chap->auth.id;
297        bp = mbuf_Read(bp, ans + 1, alen);
298        ans[alen+1] = '\0';
299        bp = auth_ReadName(&chap->auth, bp, len);
300        break;
301
302      case CHAP_SUCCESS:
303      case CHAP_FAILURE:
304        /* chap->auth.in.name is already set up at CHALLENGE time */
305        if ((ans = malloc(len + 1)) == NULL) {
306          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
307          mbuf_Free(bp);
308          return;
309        }
310        bp = mbuf_Read(bp, ans, len);
311        ans[len] = '\0';
312        break;
313    }
314
315    switch (chap->auth.in.hdr.code) {
316      case CHAP_CHALLENGE:
317      case CHAP_RESPONSE:
318        if (*chap->auth.in.name)
319          log_Printf(LogPHASE, "Chap Input: %s (from %s)\n",
320                     chapcodes[chap->auth.in.hdr.code], chap->auth.in.name);
321        else
322          log_Printf(LogPHASE, "Chap Input: %s\n",
323                     chapcodes[chap->auth.in.hdr.code]);
324        break;
325
326      case CHAP_SUCCESS:
327      case CHAP_FAILURE:
328        if (*ans)
329          log_Printf(LogPHASE, "Chap Input: %s (%s)\n",
330                     chapcodes[chap->auth.in.hdr.code], ans);
331        else
332          log_Printf(LogPHASE, "Chap Input: %s\n",
333                     chapcodes[chap->auth.in.hdr.code]);
334        break;
335    }
336
337    switch (chap->auth.in.hdr.code) {
338      case CHAP_CHALLENGE:
339        name = p->dl->bundle->cfg.auth.name;
340        nlen = strlen(name);
341        key = p->dl->bundle->cfg.auth.key;
342        myans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge, 0);
343        if (myans) {
344          ChapOutput(p, CHAP_RESPONSE, chap->auth.id, myans,
345                     *myans + 1 + nlen, name);
346          free(myans);
347        } else
348          ChapOutput(p, CHAP_FAILURE, chap->auth.id, "Out of memory!",
349                     14, NULL);
350        break;
351
352      case CHAP_RESPONSE:
353        name = chap->auth.in.name;
354        nlen = strlen(name);
355#ifndef NORADIUS
356        if (*p->dl->bundle->radius.cfg.file) {
357          chap->challenge[*chap->challenge+1] = '\0';
358          radius_Authenticate(&p->dl->bundle->radius, &chap->auth,
359                              chap->auth.in.name, ans, chap->challenge + 1);
360        } else
361#endif
362        {
363          key = auth_GetSecret(p->dl->bundle, name, nlen, p);
364          if (key) {
365            myans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge,
366                                     chap->using_MSChap);
367            if (myans == NULL)
368              key = NULL;
369            else {
370              if (*myans != alen || memcmp(myans + 1, ans + 1, *myans))
371                key = NULL;
372              free(myans);
373            }
374          }
375
376          if (key)
377            chap_Success(&chap->auth);
378          else
379            chap_Failure(&chap->auth);
380        }
381
382        break;
383
384      case CHAP_SUCCESS:
385        if (p->link.lcp.auth_iwait == PROTO_CHAP) {
386          p->link.lcp.auth_iwait = 0;
387          if (p->link.lcp.auth_ineed == 0)
388            /*
389             * We've succeeded in our ``login''
390             * If we're not expecting  the peer to authenticate (or he already
391             * has), proceed to network phase.
392             */
393            datalink_AuthOk(p->dl);
394        }
395        break;
396
397      case CHAP_FAILURE:
398        datalink_AuthNotOk(p->dl);
399        break;
400    }
401    free(ans);
402  }
403
404  mbuf_Free(bp);
405}
406