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