chap.c revision 43401
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.38 1999/01/28 01:56: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#endif 33#include <md5.h> 34#include <stdlib.h> 35#include <string.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 75static void 76ChapOutput(struct physical *physical, u_int code, u_int id, 77 const u_char * ptr, int count, const char *text) 78{ 79 int plen; 80 struct fsmheader lh; 81 struct mbuf *bp; 82 83 plen = sizeof(struct fsmheader) + count; 84 lh.code = code; 85 lh.id = id; 86 lh.length = htons(plen); 87 bp = mbuf_Alloc(plen, MB_FSM); 88 memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader)); 89 if (count) 90 memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count); 91 log_DumpBp(LogDEBUG, "ChapOutput", bp); 92 if (text == NULL) 93 log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]); 94 else 95 log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text); 96 hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp); 97} 98 99void 100chap_SendChallenge(struct authinfo *auth, int chapid, struct physical *physical) 101{ 102 struct chap *chap = auth2chap(auth); 103 int len, i; 104 char *cp; 105 106 randinit(); 107 cp = chap->challenge_data; 108 109#ifndef NORADIUS 110 if (*physical->dl->bundle->radius.cfg.file) { 111 /* For radius, our challenge is 16 readable NUL terminated bytes :*/ 112 *cp++ = chap->challenge_len = 16; 113 for (i = 0; i < chap->challenge_len; i++) 114 *cp++ = (random() % 10) + '0'; 115 } else 116#endif 117 { 118 *cp++ = chap->challenge_len = random() % (CHAPCHALLENGELEN-16) + 16; 119 for (i = 0; i < chap->challenge_len; i++) 120 *cp++ = random() & 0xff; 121 } 122 123 len = strlen(physical->dl->bundle->cfg.auth.name); 124 memcpy(cp, physical->dl->bundle->cfg.auth.name, len); 125 cp += len; 126 ChapOutput(physical, CHAP_CHALLENGE, chapid, chap->challenge_data, 127 cp - chap->challenge_data, NULL); 128} 129 130static void 131RecvChapTalk(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp, 132 struct physical *physical) 133{ 134 int valsize, len, arglen, keylen, namelen, success; 135 char *cp, *argp, *ap, *name, *digest; 136 char *keyp; 137 MD5_CTX MD5context; /* context for MD5 */ 138 char answer[CHAPDIGESTLEN]; 139 char cdigest[16]; 140#ifdef HAVE_DES 141 int ix; 142 MD4_CTX MD4context; /* context for MD4 */ 143#endif 144 145 len = ntohs(chp->length); 146 log_Printf(LogDEBUG, "RecvChapTalk: length: %d\n", len); 147 arglen = len - sizeof(struct fsmheader); 148 cp = (char *) MBUF_CTOP(bp); 149 valsize = *cp++ & 255; 150 name = cp + valsize; 151 namelen = arglen - valsize - 1; 152 name[namelen] = 0; 153 154 log_Printf(LogPHASE, "Chap Input: %s (from %s)\n", 155 chapcodes[chp->code], name); 156 157 switch (chp->code) { 158 case CHAP_CHALLENGE: 159 keyp = bundle->cfg.auth.key; 160 keylen = strlen(bundle->cfg.auth.key); 161 name = bundle->cfg.auth.name; 162 namelen = strlen(bundle->cfg.auth.name); 163 164#ifdef HAVE_DES 165 if (physical->dl->chap.using_MSChap) 166 argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN); 167 else 168#endif 169 argp = malloc(1 + valsize + namelen + 16); 170 171 if (argp == NULL) { 172 ChapOutput(physical, CHAP_FAILURE, chp->id, "Out of memory!", 14, NULL); 173 return; 174 } 175#ifdef HAVE_DES 176 if (physical->dl->chap.using_MSChap) { 177 digest = argp; /* this is the response */ 178 *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */ 179 memset(digest, '\0', 24); 180 digest += 24; 181 182 ap = answer; /* this is the challenge */ 183 memcpy(ap, keyp, keylen); 184 ap += 2 * keylen; 185 memcpy(ap, cp, valsize); 186 log_DumpBuff(LogDEBUG, "recv", ap, valsize); 187 ap += valsize; 188 for (ix = keylen; ix > 0 ; ix--) { 189 answer[2*ix-2] = answer[ix-1]; 190 answer[2*ix-1] = 0; 191 } 192 MD4Init(&MD4context); 193 MD4Update(&MD4context, answer, 2 * keylen); 194 MD4Final(digest, &MD4context); 195 memcpy(digest + 25, name, namelen); 196 ap += 2 * keylen; 197 chap_MS(digest, answer + 2 * keylen, valsize); 198 log_DumpBuff(LogDEBUG, "answer", digest, 24); 199 ChapOutput(physical, CHAP_RESPONSE, chp->id, argp, 200 namelen + MS_CHAP_RESPONSE_LEN + 1, name); 201 } else { 202#endif 203 digest = argp; 204 *digest++ = 16; /* value size */ 205 ap = answer; 206 *ap++ = chp->id; 207 memcpy(ap, keyp, keylen); 208 ap += keylen; 209 memcpy(ap, cp, valsize); 210 log_DumpBuff(LogDEBUG, "recv", ap, valsize); 211 ap += valsize; 212 MD5Init(&MD5context); 213 MD5Update(&MD5context, answer, ap - answer); 214 MD5Final(digest, &MD5context); 215 log_DumpBuff(LogDEBUG, "answer", digest, 16); 216 memcpy(digest + 16, name, namelen); 217 ap += namelen; 218 /* Send answer to the peer */ 219 ChapOutput(physical, CHAP_RESPONSE, chp->id, argp, namelen + 17, name); 220#ifdef HAVE_DES 221 } 222#endif 223 free(argp); 224 if (*name == '\0') 225 log_Printf(LogWARN, "Sending empty CHAP authname!\n"); 226 break; 227 case CHAP_RESPONSE: 228 /* 229 * Get a secret key corresponds to the peer 230 */ 231 success = 0; 232#ifndef NORADIUS 233 if (*bundle->radius.cfg.file) { 234 char chapname[AUTHLEN], chal[17]; 235 236 if (namelen > AUTHLEN - 1) 237 namelen = AUTHLEN - 1; 238 strncpy(chapname, name, namelen); 239 chapname[namelen] = '\0'; 240 *answer = chp->id; 241 strncpy(answer+1, cp, 16); 242 answer[17] = '\0'; 243 strncpy(chal, physical->dl->chap.challenge_data + 1, 16); 244 chal[16] = '\0'; 245 246 if (radius_Authenticate(&bundle->radius, bundle, chapname, answer, chal)) 247 success = 1; /* And there was much rejoicing ! */ 248 249 } else 250#endif 251 if ((keyp = auth_GetSecret(bundle, name, namelen, physical))) { 252 /* Compute correct digest value */ 253 keylen = strlen(keyp); 254 ap = answer; 255 *ap++ = chp->id; 256 memcpy(ap, keyp, keylen); 257 ap += keylen; 258 MD5Init(&MD5context); 259 MD5Update(&MD5context, answer, ap - answer); 260 MD5Update(&MD5context, physical->dl->chap.challenge_data + 1, 261 physical->dl->chap.challenge_len); 262 MD5Final(cdigest, &MD5context); 263 log_DumpBuff(LogDEBUG, "got", cp, 16); 264 log_DumpBuff(LogDEBUG, "expect", cdigest, 16); 265 266 /* 267 * Compare with the response 268 */ 269 if (memcmp(cp, cdigest, 16) == 0) 270 success = 1; 271 } 272 273 if (success) { 274 datalink_GotAuthname(physical->dl, name, namelen); 275 ChapOutput(physical, CHAP_SUCCESS, chp->id, "Welcome!!", 10, NULL); 276 physical->link.lcp.auth_ineed = 0; 277 if (Enabled(bundle, OPT_UTMP)) 278 physical_Login(physical, name); 279 280 if (physical->link.lcp.auth_iwait == 0) 281 /* 282 * Either I didn't need to authenticate, or I've already been 283 * told that I got the answer right. 284 */ 285 datalink_AuthOk(physical->dl); 286 } else { 287 /* 288 * Peer is not registerd, or response digest is wrong. 289 */ 290 ChapOutput(physical, CHAP_FAILURE, chp->id, "Invalid!!", 9, NULL); 291 datalink_AuthNotOk(physical->dl); 292 break; 293 } 294 } 295} 296 297static void 298RecvChapResult(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp, 299 struct physical *physical) 300{ 301 int len; 302 303 len = ntohs(chp->length); 304 log_Printf(LogDEBUG, "RecvChapResult: length: %d\n", len); 305 if (chp->code == CHAP_SUCCESS) { 306 if (physical->link.lcp.auth_iwait == PROTO_CHAP) { 307 physical->link.lcp.auth_iwait = 0; 308 if (physical->link.lcp.auth_ineed == 0) 309 /* 310 * We've succeeded in our ``login'' 311 * If we're not expecting the peer to authenticate (or he already 312 * has), proceed to network phase. 313 */ 314 datalink_AuthOk(physical->dl); 315 } 316 } else { 317 /* CHAP failed - it's not going to get any better */ 318 log_Printf(LogPHASE, "Chap Input: Giving up after name/key FAILURE\n"); 319 datalink_AuthNotOk(physical->dl); 320 } 321} 322 323void 324chap_Input(struct bundle *bundle, struct mbuf *bp, struct physical *physical) 325{ 326 int len = mbuf_Length(bp); 327 struct fsmheader *chp; 328 329 if (len >= sizeof(struct fsmheader)) { 330 chp = (struct fsmheader *) MBUF_CTOP(bp); 331 if (len >= ntohs(chp->length)) { 332 if (chp->code < 1 || chp->code > 4) 333 chp->code = 0; 334 bp->offset += sizeof(struct fsmheader); 335 bp->cnt -= sizeof(struct fsmheader); 336 337 switch (chp->code) { 338 case CHAP_RESPONSE: 339 auth_StopTimer(&physical->dl->chap.auth); 340 /* Fall into.. */ 341 case CHAP_CHALLENGE: 342 RecvChapTalk(bundle, chp, bp, physical); 343 break; 344 case CHAP_SUCCESS: 345 case CHAP_FAILURE: 346 log_Printf(LogPHASE, "Chap Input: %s\n", chapcodes[chp->code]); 347 RecvChapResult(bundle, chp, bp, physical); 348 break; 349 } 350 } 351 } 352 mbuf_Free(bp); 353} 354