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