chap.c revision 30715
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.23 1997/09/25 00:52:32 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 /* 135 * Get a secret key corresponds to the peer 136 */ 137 keyp = AuthGetSecret(SECRETFILE, name, namelen, chp->code == CHAP_RESPONSE); 138 139 switch (chp->code) { 140 case CHAP_CHALLENGE: 141 if (keyp) { 142 keylen = strlen(keyp); 143 } else { 144 keylen = strlen(VarAuthKey); 145 keyp = VarAuthKey; 146 } 147 name = VarAuthName; 148 namelen = strlen(VarAuthName); 149 150#ifdef HAVE_DES 151 if (VarMSChap) 152 argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN); 153 else 154#endif 155 argp = malloc(1 + valsize + namelen + 16); 156 157 if (argp == NULL) { 158 ChapOutput(CHAP_FAILURE, chp->id, "Out of memory!", 14); 159 return; 160 } 161#ifdef HAVE_DES 162 if (VarMSChap) { 163 digest = argp; /* this is the response */ 164 *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */ 165 memset(digest, '\0', 24); 166 digest += 24; 167 168 ap = answer; /* this is the challenge */ 169 memcpy(ap, keyp, keylen); 170 ap += 2 * keylen; 171 memcpy(ap, cp, valsize); 172 LogDumpBuff(LogDEBUG, "recv", ap, valsize); 173 ap += valsize; 174 for (ix = keylen; ix > 0 ; ix--) { 175 answer[2*ix-2] = answer[ix-1]; 176 answer[2*ix-1] = 0; 177 } 178 MD4Init(&MD4context); 179 MD4Update(&MD4context, answer, 2 * keylen); 180 MD4Final(digest, &MD4context); 181 memcpy(digest + 25, name, namelen); 182 ap += 2 * keylen; 183 ChapMS(digest, answer + 2 * keylen, valsize); 184 LogDumpBuff(LogDEBUG, "answer", digest, 24); 185 ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + MS_CHAP_RESPONSE_LEN + 1); 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 LogDumpBuff(LogDEBUG, "recv", ap, valsize); 196 ap += valsize; 197 MD5Init(&MD5context); 198 MD5Update(&MD5context, answer, ap - answer); 199 MD5Final(digest, &MD5context); 200 LogDumpBuff(LogDEBUG, "answer", digest, 16); 201 memcpy(digest + 16, name, namelen); 202 ap += namelen; 203 /* Send answer to the peer */ 204 ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + 17); 205#ifdef HAVE_DES 206 } 207#endif 208 free(argp); 209 break; 210 case CHAP_RESPONSE: 211 if (keyp) { 212 213 /* 214 * Compute correct digest value 215 */ 216 keylen = strlen(keyp); 217 ap = answer; 218 *ap++ = chp->id; 219 memcpy(ap, keyp, keylen); 220 ap += keylen; 221 MD5Init(&MD5context); 222 MD5Update(&MD5context, answer, ap - answer); 223 MD5Update(&MD5context, challenge_data + 1, challenge_len); 224 MD5Final(cdigest, &MD5context); 225 LogDumpBuff(LogDEBUG, "got", cp, 16); 226 LogDumpBuff(LogDEBUG, "expect", cdigest, 16); 227 228 /* 229 * Compare with the response 230 */ 231 if (memcmp(cp, cdigest, 16) == 0) { 232 ChapOutput(CHAP_SUCCESS, chp->id, "Welcome!!", 10); 233 if ((mode & MODE_DIRECT) && isatty(modem) && Enabled(ConfUtmp)) 234 if (Utmp) 235 LogPrintf(LogERROR, "Oops, already logged in on %s\n", 236 VarBaseDevice); 237 else { 238 struct utmp ut; 239 memset(&ut, 0, sizeof(ut)); 240 time(&ut.ut_time); 241 strncpy(ut.ut_name, name, sizeof(ut.ut_name)-1); 242 strncpy(ut.ut_line, VarBaseDevice, sizeof(ut.ut_line)-1); 243 if (logout(ut.ut_line)) 244 logwtmp(ut.ut_line, "", ""); 245 login(&ut); 246 Utmp = 1; 247 } 248 NewPhase(PHASE_NETWORK); 249 break; 250 } 251 } 252 253 /* 254 * Peer is not registerd, or response digest is wrong. 255 */ 256 ChapOutput(CHAP_FAILURE, chp->id, "Invalid!!", 9); 257 reconnect(RECON_FALSE); 258 LcpClose(); 259 break; 260 } 261} 262 263static void 264RecvChapResult(struct fsmheader *chp, struct mbuf *bp) 265{ 266 int len; 267 struct lcpstate *lcp = &LcpInfo; 268 269 len = ntohs(chp->length); 270 LogPrintf(LogDEBUG, "RecvChapResult: length: %d\n", len); 271 if (chp->code == CHAP_SUCCESS) { 272 if (lcp->auth_iwait == PROTO_CHAP) { 273 lcp->auth_iwait = 0; 274 if (lcp->auth_ineed == 0) 275 NewPhase(PHASE_NETWORK); 276 } 277 } else { 278 279 /* 280 * Maybe, we shoud close LCP. Of cause, peer may take close action, too. 281 */ 282 ; 283 } 284} 285 286void 287ChapInput(struct mbuf *bp) 288{ 289 int len = plength(bp); 290 struct fsmheader *chp; 291 292 if (len >= sizeof(struct fsmheader)) { 293 chp = (struct fsmheader *) MBUF_CTOP(bp); 294 if (len >= ntohs(chp->length)) { 295 if (chp->code < 1 || chp->code > 4) 296 chp->code = 0; 297 LogPrintf(LogLCP, "ChapInput: %s\n", chapcodes[chp->code]); 298 299 bp->offset += sizeof(struct fsmheader); 300 bp->cnt -= sizeof(struct fsmheader); 301 302 switch (chp->code) { 303 case CHAP_RESPONSE: 304 StopAuthTimer(&AuthChapInfo); 305 /* Fall into.. */ 306 case CHAP_CHALLENGE: 307 RecvChapTalk(chp, bp); 308 break; 309 case CHAP_SUCCESS: 310 case CHAP_FAILURE: 311 RecvChapResult(chp, bp); 312 break; 313 } 314 } 315 } 316 pfree(bp); 317} 318