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