chap.c revision 31051
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.24 1997/10/26 01:02:16 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 "mbuf.h" 45#include "log.h" 46#include "defs.h" 47#include "timer.h" 48#include "fsm.h" 49#include "chap.h" 50#include "chap_ms.h" 51#include "lcpproto.h" 52#include "lcp.h" 53#include "hdlc.h" 54#include "phase.h" 55#include "loadalias.h" 56#include "command.h" 57#include "vars.h" 58#include "auth.h" 59 60static char *chapcodes[] = { 61 "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE" 62}; 63 64static void 65ChapOutput(u_int code, u_int id, u_char * ptr, int count) 66{ 67 int plen; 68 struct fsmheader lh; 69 struct mbuf *bp; 70 71 plen = sizeof(struct fsmheader) + count; 72 lh.code = code; 73 lh.id = id; 74 lh.length = htons(plen); 75 bp = mballoc(plen, MB_FSM); 76 memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader)); 77 if (count) 78 memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count); 79 LogDumpBp(LogDEBUG, "ChapOutput", bp); 80 LogPrintf(LogLCP, "ChapOutput: %s\n", chapcodes[code]); 81 HdlcOutput(PRI_LINK, PROTO_CHAP, bp); 82} 83 84 85static char challenge_data[80]; 86static int challenge_len; 87 88static void 89SendChapChallenge(int chapid) 90{ 91 int len, i; 92 char *cp; 93 94 randinit(); 95 cp = challenge_data; 96 *cp++ = challenge_len = random() % 32 + 16; 97 for (i = 0; i < challenge_len; i++) 98 *cp++ = random() & 0xff; 99 len = strlen(VarAuthName); 100 memcpy(cp, VarAuthName, len); 101 cp += len; 102 ChapOutput(CHAP_CHALLENGE, chapid, challenge_data, cp - challenge_data); 103} 104 105struct authinfo AuthChapInfo = { 106 SendChapChallenge, 107}; 108 109static void 110RecvChapTalk(struct fsmheader *chp, struct mbuf *bp) 111{ 112 int valsize, len; 113 int arglen, keylen, namelen; 114 char *cp, *argp, *ap, *name, *digest; 115 char *keyp; 116 MD5_CTX MD5context; /* context for MD5 */ 117 char answer[100]; 118 char cdigest[16]; 119#ifdef HAVE_DES 120 int ix; 121 MD4_CTX MD4context; /* context for MD4 */ 122#endif 123 124 len = ntohs(chp->length); 125 LogPrintf(LogDEBUG, "RecvChapTalk: length: %d\n", len); 126 arglen = len - sizeof(struct fsmheader); 127 cp = (char *) MBUF_CTOP(bp); 128 valsize = *cp++ & 255; 129 name = cp + valsize; 130 namelen = arglen - valsize - 1; 131 name[namelen] = 0; 132 LogPrintf(LogPHASE, " Valsize = %d, Name = %s\n", valsize, name); 133 134 switch (chp->code) { 135 case CHAP_CHALLENGE: 136 keyp = VarAuthKey; 137 keylen = strlen(VarAuthKey); 138 name = VarAuthName; 139 namelen = strlen(VarAuthName); 140 141#ifdef HAVE_DES 142 if (VarMSChap) 143 argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN); 144 else 145#endif 146 argp = malloc(1 + valsize + namelen + 16); 147 148 if (argp == NULL) { 149 ChapOutput(CHAP_FAILURE, chp->id, "Out of memory!", 14); 150 return; 151 } 152#ifdef HAVE_DES 153 if (VarMSChap) { 154 digest = argp; /* this is the response */ 155 *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */ 156 memset(digest, '\0', 24); 157 digest += 24; 158 159 ap = answer; /* this is the challenge */ 160 memcpy(ap, keyp, keylen); 161 ap += 2 * keylen; 162 memcpy(ap, cp, valsize); 163 LogDumpBuff(LogDEBUG, "recv", ap, valsize); 164 ap += valsize; 165 for (ix = keylen; ix > 0 ; ix--) { 166 answer[2*ix-2] = answer[ix-1]; 167 answer[2*ix-1] = 0; 168 } 169 MD4Init(&MD4context); 170 MD4Update(&MD4context, answer, 2 * keylen); 171 MD4Final(digest, &MD4context); 172 memcpy(digest + 25, name, namelen); 173 ap += 2 * keylen; 174 ChapMS(digest, answer + 2 * keylen, valsize); 175 LogDumpBuff(LogDEBUG, "answer", digest, 24); 176 ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + MS_CHAP_RESPONSE_LEN + 1); 177 } else { 178#endif 179 digest = argp; 180 *digest++ = 16; /* value size */ 181 ap = answer; 182 *ap++ = chp->id; 183 memcpy(ap, keyp, keylen); 184 ap += keylen; 185 memcpy(ap, cp, valsize); 186 LogDumpBuff(LogDEBUG, "recv", ap, valsize); 187 ap += valsize; 188 MD5Init(&MD5context); 189 MD5Update(&MD5context, answer, ap - answer); 190 MD5Final(digest, &MD5context); 191 LogDumpBuff(LogDEBUG, "answer", digest, 16); 192 memcpy(digest + 16, name, namelen); 193 ap += namelen; 194 /* Send answer to the peer */ 195 ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + 17); 196#ifdef HAVE_DES 197 } 198#endif 199 free(argp); 200 break; 201 case CHAP_RESPONSE: 202 /* 203 * Get a secret key corresponds to the peer 204 */ 205 keyp = AuthGetSecret(SECRETFILE, name, namelen, chp->code == CHAP_RESPONSE); 206 if (keyp) { 207 /* 208 * Compute correct digest value 209 */ 210 keylen = strlen(keyp); 211 ap = answer; 212 *ap++ = chp->id; 213 memcpy(ap, keyp, keylen); 214 ap += keylen; 215 MD5Init(&MD5context); 216 MD5Update(&MD5context, answer, ap - answer); 217 MD5Update(&MD5context, challenge_data + 1, challenge_len); 218 MD5Final(cdigest, &MD5context); 219 LogDumpBuff(LogDEBUG, "got", cp, 16); 220 LogDumpBuff(LogDEBUG, "expect", cdigest, 16); 221 222 /* 223 * Compare with the response 224 */ 225 if (memcmp(cp, cdigest, 16) == 0) { 226 ChapOutput(CHAP_SUCCESS, chp->id, "Welcome!!", 10); 227 if ((mode & MODE_DIRECT) && isatty(modem) && Enabled(ConfUtmp)) 228 if (Utmp) 229 LogPrintf(LogERROR, "Oops, already logged in on %s\n", 230 VarBaseDevice); 231 else { 232 struct utmp ut; 233 memset(&ut, 0, sizeof(ut)); 234 time(&ut.ut_time); 235 strncpy(ut.ut_name, name, sizeof(ut.ut_name)-1); 236 strncpy(ut.ut_line, VarBaseDevice, sizeof(ut.ut_line)-1); 237 if (logout(ut.ut_line)) 238 logwtmp(ut.ut_line, "", ""); 239 login(&ut); 240 Utmp = 1; 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