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