chap.c revision 34536
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.29 1998/02/19 02:10:06 brian Exp $ 21 * 22 * TODO: 23 */ 24#include <sys/param.h> 25#include <netinet/in.h> 26 27#include <ctype.h> 28#ifdef HAVE_DES 29#include <md4.h> 30#endif 31#include <md5.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <time.h> 36#include <unistd.h> 37#ifdef __OpenBSD__ 38#include <util.h> 39#else 40#include <libutil.h> 41#endif 42#include <utmp.h> 43 44#include "command.h" 45#include "mbuf.h" 46#include "log.h" 47#include "defs.h" 48#include "timer.h" 49#include "fsm.h" 50#include "chap.h" 51#include "chap_ms.h" 52#include "lcpproto.h" 53#include "lcp.h" 54#include "hdlc.h" 55#include "phase.h" 56#include "loadalias.h" 57#include "vars.h" 58#include "auth.h" 59#include "id.h" 60 61static const char *chapcodes[] = { 62 "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE" 63}; 64 65static void 66ChapOutput(u_int code, u_int id, const u_char * ptr, int count) 67{ 68 int plen; 69 struct fsmheader lh; 70 struct mbuf *bp; 71 72 plen = sizeof(struct fsmheader) + count; 73 lh.code = code; 74 lh.id = id; 75 lh.length = htons(plen); 76 bp = mballoc(plen, MB_FSM); 77 memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader)); 78 if (count) 79 memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count); 80 LogDumpBp(LogDEBUG, "ChapOutput", bp); 81 LogPrintf(LogLCP, "ChapOutput: %s\n", chapcodes[code]); 82 HdlcOutput(PRI_LINK, PROTO_CHAP, bp); 83} 84 85 86static char challenge_data[80]; 87static int challenge_len; 88 89static void 90SendChapChallenge(int chapid) 91{ 92 int len, i; 93 char *cp; 94 95 randinit(); 96 cp = challenge_data; 97 *cp++ = challenge_len = random() % 32 + 16; 98 for (i = 0; i < challenge_len; i++) 99 *cp++ = random() & 0xff; 100 len = strlen(VarAuthName); 101 memcpy(cp, VarAuthName, len); 102 cp += len; 103 ChapOutput(CHAP_CHALLENGE, chapid, challenge_data, cp - challenge_data); 104} 105 106struct authinfo AuthChapInfo = { 107 SendChapChallenge, 108}; 109 110static void 111RecvChapTalk(struct fsmheader *chp, struct mbuf *bp) 112{ 113 int valsize, len; 114 int arglen, keylen, namelen; 115 char *cp, *argp, *ap, *name, *digest; 116 char *keyp; 117 MD5_CTX MD5context; /* context for MD5 */ 118 char answer[100]; 119 char cdigest[16]; 120#ifdef HAVE_DES 121 int ix; 122 MD4_CTX MD4context; /* context for MD4 */ 123#endif 124 125 len = ntohs(chp->length); 126 LogPrintf(LogDEBUG, "RecvChapTalk: length: %d\n", len); 127 arglen = len - sizeof(struct fsmheader); 128 cp = (char *) MBUF_CTOP(bp); 129 valsize = *cp++ & 255; 130 name = cp + valsize; 131 namelen = arglen - valsize - 1; 132 name[namelen] = 0; 133 LogPrintf(LogLCP, " Valsize = %d, Name = \"%s\"\n", valsize, name); 134 135 switch (chp->code) { 136 case CHAP_CHALLENGE: 137 keyp = VarAuthKey; 138 keylen = strlen(VarAuthKey); 139 name = VarAuthName; 140 namelen = strlen(VarAuthName); 141 142#ifdef HAVE_DES 143 if (VarMSChap) 144 argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN); 145 else 146#endif 147 argp = malloc(1 + valsize + namelen + 16); 148 149 if (argp == NULL) { 150 ChapOutput(CHAP_FAILURE, chp->id, "Out of memory!", 14); 151 return; 152 } 153#ifdef HAVE_DES 154 if (VarMSChap) { 155 digest = argp; /* this is the response */ 156 *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */ 157 memset(digest, '\0', 24); 158 digest += 24; 159 160 ap = answer; /* this is the challenge */ 161 memcpy(ap, keyp, keylen); 162 ap += 2 * keylen; 163 memcpy(ap, cp, valsize); 164 LogDumpBuff(LogDEBUG, "recv", ap, valsize); 165 ap += valsize; 166 for (ix = keylen; ix > 0 ; ix--) { 167 answer[2*ix-2] = answer[ix-1]; 168 answer[2*ix-1] = 0; 169 } 170 MD4Init(&MD4context); 171 MD4Update(&MD4context, answer, 2 * keylen); 172 MD4Final(digest, &MD4context); 173 memcpy(digest + 25, name, namelen); 174 ap += 2 * keylen; 175 ChapMS(digest, answer + 2 * keylen, valsize); 176 LogDumpBuff(LogDEBUG, "answer", digest, 24); 177 ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + MS_CHAP_RESPONSE_LEN + 1); 178 } else { 179#endif 180 digest = argp; 181 *digest++ = 16; /* value size */ 182 ap = answer; 183 *ap++ = chp->id; 184 memcpy(ap, keyp, keylen); 185 ap += keylen; 186 memcpy(ap, cp, valsize); 187 LogDumpBuff(LogDEBUG, "recv", ap, valsize); 188 ap += valsize; 189 MD5Init(&MD5context); 190 MD5Update(&MD5context, answer, ap - answer); 191 MD5Final(digest, &MD5context); 192 LogDumpBuff(LogDEBUG, "answer", digest, 16); 193 memcpy(digest + 16, name, namelen); 194 ap += namelen; 195 /* Send answer to the peer */ 196 ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + 17); 197#ifdef HAVE_DES 198 } 199#endif 200 free(argp); 201 break; 202 case CHAP_RESPONSE: 203 /* 204 * Get a secret key corresponds to the peer 205 */ 206 keyp = AuthGetSecret(SECRETFILE, name, namelen, chp->code == CHAP_RESPONSE); 207 if (keyp) { 208 /* 209 * Compute correct digest value 210 */ 211 keylen = strlen(keyp); 212 ap = answer; 213 *ap++ = chp->id; 214 memcpy(ap, keyp, keylen); 215 ap += keylen; 216 MD5Init(&MD5context); 217 MD5Update(&MD5context, answer, ap - answer); 218 MD5Update(&MD5context, challenge_data + 1, challenge_len); 219 MD5Final(cdigest, &MD5context); 220 LogDumpBuff(LogDEBUG, "got", cp, 16); 221 LogDumpBuff(LogDEBUG, "expect", cdigest, 16); 222 223 /* 224 * Compare with the response 225 */ 226 if (memcmp(cp, cdigest, 16) == 0) { 227 ChapOutput(CHAP_SUCCESS, chp->id, "Welcome!!", 10); 228 if ((mode & MODE_DIRECT) && isatty(modem) && Enabled(ConfUtmp)) { 229 if (Utmp) 230 LogPrintf(LogERROR, "Oops, already logged in on %s\n", 231 VarBaseDevice); 232 else { 233 struct utmp ut; 234 memset(&ut, 0, sizeof ut); 235 time(&ut.ut_time); 236 strncpy(ut.ut_name, name, sizeof ut.ut_name); 237 strncpy(ut.ut_line, VarBaseDevice, sizeof ut.ut_line - 1); 238 ID0login(&ut); 239 Utmp = 1; 240 } 241 } 242 NewPhase(PHASE_NETWORK); 243 break; 244 } 245 } 246 247 /* 248 * Peer is not registerd, or response digest is wrong. 249 */ 250 ChapOutput(CHAP_FAILURE, chp->id, "Invalid!!", 9); 251 reconnect(RECON_FALSE); 252 LcpClose(); 253 break; 254 } 255} 256 257static void 258RecvChapResult(struct fsmheader *chp, struct mbuf *bp) 259{ 260 int len; 261 struct lcpstate *lcp = &LcpInfo; 262 263 len = ntohs(chp->length); 264 LogPrintf(LogDEBUG, "RecvChapResult: length: %d\n", len); 265 if (chp->code == CHAP_SUCCESS) { 266 if (lcp->auth_iwait == PROTO_CHAP) { 267 lcp->auth_iwait = 0; 268 if (lcp->auth_ineed == 0) 269 NewPhase(PHASE_NETWORK); 270 } 271 } else { 272 273 /* 274 * Maybe, we shoud close LCP. Of cause, peer may take close action, too. 275 */ 276 ; 277 } 278} 279 280void 281ChapInput(struct mbuf *bp) 282{ 283 int len = plength(bp); 284 struct fsmheader *chp; 285 286 if (len >= sizeof(struct fsmheader)) { 287 chp = (struct fsmheader *) MBUF_CTOP(bp); 288 if (len >= ntohs(chp->length)) { 289 if (chp->code < 1 || chp->code > 4) 290 chp->code = 0; 291 LogPrintf(LogLCP, "ChapInput: %s\n", chapcodes[chp->code]); 292 293 bp->offset += sizeof(struct fsmheader); 294 bp->cnt -= sizeof(struct fsmheader); 295 296 switch (chp->code) { 297 case CHAP_RESPONSE: 298 StopAuthTimer(&AuthChapInfo); 299 /* Fall into.. */ 300 case CHAP_CHALLENGE: 301 RecvChapTalk(chp, bp); 302 break; 303 case CHAP_SUCCESS: 304 case CHAP_FAILURE: 305 RecvChapResult(chp, bp); 306 break; 307 } 308 } 309 } 310 pfree(bp); 311} 312