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