chap.c revision 45220
16059Samurai/* 26059Samurai * PPP CHAP Module 36059Samurai * 46059Samurai * Written by Toshiharu OHNO (tony-o@iij.ad.jp) 56059Samurai * 66059Samurai * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd. 76059Samurai * 86059Samurai * Redistribution and use in source and binary forms are permitted 96059Samurai * provided that the above copyright notice and this paragraph are 106059Samurai * duplicated in all such forms and that any documentation, 116059Samurai * advertising materials, and other materials related to such 126059Samurai * distribution and use acknowledge that the software was developed 136059Samurai * by the Internet Initiative Japan, Inc. The name of the 146059Samurai * IIJ may not be used to endorse or promote products derived 156059Samurai * from this software without specific prior written permission. 166059Samurai * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 176059Samurai * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 186059Samurai * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 198857Srgrimes * 2045220Sbrian * $Id: chap.c,v 1.47 1999/02/20 01:12:45 brian Exp $ 218857Srgrimes * 226059Samurai * TODO: 236059Samurai */ 2443313Sbrian#include <sys/param.h> 2530715Sbrian#include <netinet/in.h> 2636285Sbrian#include <netinet/in_systm.h> 2736285Sbrian#include <netinet/ip.h> 2836285Sbrian#include <sys/un.h> 2930715Sbrian 3043888Sbrian#include <errno.h> 3143888Sbrian#include <fcntl.h> 3237192Sbrian#ifdef HAVE_DES 3336287Sbrian#include <md4.h> 3437192Sbrian#endif 3530715Sbrian#include <md5.h> 3643888Sbrian#include <paths.h> 3743888Sbrian#include <signal.h> 3830715Sbrian#include <stdlib.h> 3944106Sbrian#include <string.h> 4043888Sbrian#include <sys/wait.h> 4136285Sbrian#include <termios.h> 4243888Sbrian#include <unistd.h> 4329840Sbrian 4430715Sbrian#include "mbuf.h" 4530715Sbrian#include "log.h" 4630715Sbrian#include "defs.h" 4730715Sbrian#include "timer.h" 486059Samurai#include "fsm.h" 496059Samurai#include "lcpproto.h" 506059Samurai#include "lcp.h" 5136285Sbrian#include "lqr.h" 526059Samurai#include "hdlc.h" 536735Samurai#include "auth.h" 5436285Sbrian#include "async.h" 5536285Sbrian#include "throughput.h" 5636285Sbrian#include "descriptor.h" 5743888Sbrian#include "chap.h" 5836285Sbrian#include "iplist.h" 5936285Sbrian#include "slcompress.h" 6036285Sbrian#include "ipcp.h" 6136285Sbrian#include "filter.h" 6236285Sbrian#include "ccp.h" 6336285Sbrian#include "link.h" 6436285Sbrian#include "physical.h" 6536285Sbrian#include "mp.h" 6643313Sbrian#ifndef NORADIUS 6743313Sbrian#include "radius.h" 6843313Sbrian#endif 6936285Sbrian#include "bundle.h" 7036285Sbrian#include "chat.h" 7138174Sbrian#include "cbcp.h" 7243888Sbrian#include "command.h" 7336285Sbrian#include "datalink.h" 7437192Sbrian#ifdef HAVE_DES 7536287Sbrian#include "chap_ms.h" 7637192Sbrian#endif 776059Samurai 7831343Sbrianstatic const char *chapcodes[] = { 7919866Sphk "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE" 806059Samurai}; 8143693Sbrian#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1) 826059Samurai 8330715Sbrianstatic void 8436285SbrianChapOutput(struct physical *physical, u_int code, u_int id, 8543693Sbrian const u_char *ptr, int count, const char *text) 866059Samurai{ 876059Samurai int plen; 886059Samurai struct fsmheader lh; 896059Samurai struct mbuf *bp; 906059Samurai 9128679Sbrian plen = sizeof(struct fsmheader) + count; 926059Samurai lh.code = code; 936059Samurai lh.id = id; 946059Samurai lh.length = htons(plen); 9536285Sbrian bp = mbuf_Alloc(plen, MB_FSM); 9630715Sbrian memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader)); 976059Samurai if (count) 9830715Sbrian memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count); 9936285Sbrian log_DumpBp(LogDEBUG, "ChapOutput", bp); 10037926Sbrian if (text == NULL) 10137926Sbrian log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]); 10237926Sbrian else 10337926Sbrian log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text); 10436285Sbrian hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp); 1056059Samurai} 1066059Samurai 10743693Sbrianstatic char * 10844123Sbrianchap_BuildAnswer(char *name, char *key, u_char id, char *challenge, u_char type 10944123Sbrian#ifdef HAVE_DES 11044123Sbrian , int lanman 11144123Sbrian#endif 11244123Sbrian ) 1136059Samurai{ 11443693Sbrian char *result, *digest; 11543693Sbrian size_t nlen, klen; 11643693Sbrian 11743693Sbrian nlen = strlen(name); 11843693Sbrian klen = strlen(key); 11943693Sbrian 12043693Sbrian#ifdef HAVE_DES 12144106Sbrian if (type == 0x80) { 12243693Sbrian char expkey[AUTHLEN << 2]; 12343693Sbrian MD4_CTX MD4context; 12443693Sbrian int f; 12543693Sbrian 12643693Sbrian if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL) 12743693Sbrian return result; 12843693Sbrian 12944106Sbrian digest = result; /* the response */ 13044106Sbrian *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */ 13144106Sbrian memcpy(digest + MS_CHAP_RESPONSE_LEN, name, nlen); 13244106Sbrian if (lanman) { 13344106Sbrian memset(digest + 24, '\0', 25); 13444106Sbrian mschap_LANMan(digest, challenge + 1, key); /* LANMan response */ 13544106Sbrian } else { 13644106Sbrian memset(digest, '\0', 25); 13744106Sbrian digest += 24; 13843693Sbrian 13944106Sbrian for (f = 0; f < klen; f++) { 14044106Sbrian expkey[2*f] = key[f]; 14144106Sbrian expkey[2*f+1] = '\0'; 14244106Sbrian } 14344106Sbrian /* 14444106Sbrian * ----------- 14544106Sbrian * expkey = | k\0e\0y\0 | 14644106Sbrian * ----------- 14744106Sbrian */ 14844106Sbrian MD4Init(&MD4context); 14944106Sbrian MD4Update(&MD4context, expkey, klen << 1); 15044106Sbrian MD4Final(digest, &MD4context); 15144106Sbrian 15244106Sbrian /* 15344106Sbrian * ---- -------- ---------------- ------- ------ 15444106Sbrian * result = | 49 | LANMan | 16 byte digest | 9 * ? | name | 15544106Sbrian * ---- -------- ---------------- ------- ------ 15644106Sbrian */ 15744106Sbrian mschap_NT(digest, challenge + 1); 15843693Sbrian } 15943693Sbrian /* 16044106Sbrian * ---- -------- ------------- ----- ------ 16144106Sbrian * | | struct MS_ChapResponse24 | | 16244106Sbrian * result = | 49 | LANMan | NT digest | 0/1 | name | 16344106Sbrian * ---- -------- ------------- ----- ------ 16444106Sbrian * where only one of LANMan & NT digest are set. 16543693Sbrian */ 16643693Sbrian } else 16743693Sbrian#endif 16843693Sbrian if ((result = malloc(nlen + 17)) != NULL) { 16943693Sbrian /* Normal MD5 stuff */ 17043693Sbrian MD5_CTX MD5context; 17143693Sbrian 17243693Sbrian digest = result; 17343693Sbrian *digest++ = 16; /* value size */ 17443693Sbrian 17543693Sbrian MD5Init(&MD5context); 17643693Sbrian MD5Update(&MD5context, &id, 1); 17743693Sbrian MD5Update(&MD5context, key, klen); 17843693Sbrian MD5Update(&MD5context, challenge + 1, *challenge); 17943693Sbrian MD5Final(digest, &MD5context); 18043693Sbrian 18143693Sbrian memcpy(digest + 16, name, nlen); 18243693Sbrian /* 18343693Sbrian * ---- -------- ------ 18443693Sbrian * result = | 16 | digest | name | 18543693Sbrian * ---- -------- ------ 18643693Sbrian */ 18743693Sbrian } 18843693Sbrian 18943693Sbrian return result; 19043693Sbrian} 19143693Sbrian 19243693Sbrianstatic void 19343888Sbrianchap_StartChild(struct chap *chap, char *prog, const char *name) 19443888Sbrian{ 19543888Sbrian char *argv[MAXARGS], *nargv[MAXARGS]; 19643888Sbrian int argc, fd; 19743888Sbrian int in[2], out[2]; 19843888Sbrian 19943888Sbrian if (chap->child.fd != -1) { 20043888Sbrian log_Printf(LogWARN, "Chap: %s: Program already running\n", prog); 20143888Sbrian return; 20243888Sbrian } 20343888Sbrian 20443888Sbrian if (pipe(in) == -1) { 20543888Sbrian log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno)); 20643888Sbrian return; 20743888Sbrian } 20843888Sbrian 20943888Sbrian if (pipe(out) == -1) { 21043888Sbrian log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno)); 21143888Sbrian close(in[0]); 21243888Sbrian close(in[1]); 21343888Sbrian return; 21443888Sbrian } 21543888Sbrian 21643888Sbrian switch ((chap->child.pid = fork())) { 21743888Sbrian case -1: 21843888Sbrian log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno)); 21943888Sbrian close(in[0]); 22043888Sbrian close(in[1]); 22143888Sbrian close(out[0]); 22243888Sbrian close(out[1]); 22343888Sbrian chap->child.pid = 0; 22443888Sbrian return; 22543888Sbrian 22643888Sbrian case 0: 22743888Sbrian timer_TermService(); 22843888Sbrian close(in[1]); 22943888Sbrian close(out[0]); 23043888Sbrian if (out[1] == STDIN_FILENO) { 23143888Sbrian fd = dup(out[1]); 23243888Sbrian close(out[1]); 23343888Sbrian out[1] = fd; 23443888Sbrian } 23543888Sbrian dup2(in[0], STDIN_FILENO); 23643888Sbrian dup2(out[1], STDOUT_FILENO); 23743888Sbrian if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) { 23843888Sbrian log_Printf(LogALERT, "Chap: Failed to open %s: %s\n", 23943888Sbrian _PATH_DEVNULL, strerror(errno)); 24043888Sbrian exit(1); 24143888Sbrian } 24243888Sbrian dup2(fd, STDERR_FILENO); 24343888Sbrian fcntl(3, F_SETFD, 1); /* Set close-on-exec flag */ 24443888Sbrian 24543888Sbrian setuid(geteuid()); 24643888Sbrian argc = command_Interpret(prog, strlen(prog), argv); 24743888Sbrian command_Expand(nargv, argc, (char const *const *)argv, 24843888Sbrian chap->auth.physical->dl->bundle, 0); 24943888Sbrian execvp(nargv[0], nargv); 25043888Sbrian 25143888Sbrian log_Printf(LogWARN, "exec() of %s failed: %s\n", 25243888Sbrian nargv[0], strerror(errno)); 25343888Sbrian exit(255); 25443888Sbrian 25543888Sbrian default: 25643888Sbrian close(in[0]); 25743888Sbrian close(out[1]); 25843888Sbrian chap->child.fd = out[0]; 25943888Sbrian chap->child.buf.len = 0; 26043888Sbrian write(in[1], chap->auth.in.name, strlen(chap->auth.in.name)); 26143888Sbrian write(in[1], "\n", 1); 26243888Sbrian write(in[1], chap->challenge + 1, *chap->challenge); 26343888Sbrian write(in[1], "\n", 1); 26443888Sbrian write(in[1], name, strlen(name)); 26543888Sbrian write(in[1], "\n", 1); 26643888Sbrian close(in[1]); 26743888Sbrian break; 26843888Sbrian } 26943888Sbrian} 27043888Sbrian 27143888Sbrianstatic void 27243888Sbrianchap_Cleanup(struct chap *chap, int sig) 27343888Sbrian{ 27443888Sbrian if (chap->child.pid) { 27543888Sbrian int status; 27643888Sbrian 27743888Sbrian close(chap->child.fd); 27843888Sbrian chap->child.fd = -1; 27943888Sbrian if (sig) 28043888Sbrian kill(chap->child.pid, SIGTERM); 28143888Sbrian chap->child.pid = 0; 28243888Sbrian chap->child.buf.len = 0; 28343888Sbrian 28443888Sbrian if (wait(&status) == -1) 28543888Sbrian log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno)); 28643888Sbrian else if (WIFSIGNALED(status)) 28743888Sbrian log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status)); 28843888Sbrian else if (WIFEXITED(status) && WEXITSTATUS(status)) 28943888Sbrian log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status)); 29043888Sbrian } 29143888Sbrian *chap->challenge = 0; 29244123Sbrian#ifdef HAVE_DES 29344106Sbrian chap->peertries = 0; 29444123Sbrian#endif 29543888Sbrian} 29643888Sbrian 29743888Sbrianstatic void 29844123Sbrianchap_Respond(struct chap *chap, char *name, char *key, u_char type 29944123Sbrian#ifdef HAVE_DES 30044123Sbrian , int lm 30144123Sbrian#endif 30244123Sbrian ) 30343888Sbrian{ 30444106Sbrian u_char *ans; 30543888Sbrian 30644123Sbrian ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge, type 30744123Sbrian#ifdef HAVE_DES 30844123Sbrian , lm 30944123Sbrian#endif 31044123Sbrian ); 31143888Sbrian 31243888Sbrian if (ans) { 31343888Sbrian ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id, 31443888Sbrian ans, *ans + 1 + strlen(name), name); 31544123Sbrian#ifdef HAVE_DES 31644106Sbrian chap->NTRespSent = !lm; 31744123Sbrian#endif 31843888Sbrian free(ans); 31943888Sbrian } else 32043888Sbrian ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id, 32143888Sbrian "Out of memory!", 14, NULL); 32243888Sbrian} 32343888Sbrian 32443888Sbrianstatic int 32543888Sbrianchap_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 32643888Sbrian{ 32743888Sbrian struct chap *chap = descriptor2chap(d); 32843888Sbrian 32943888Sbrian if (r && chap && chap->child.fd != -1) { 33043888Sbrian FD_SET(chap->child.fd, r); 33143888Sbrian if (*n < chap->child.fd + 1) 33243888Sbrian *n = chap->child.fd + 1; 33343888Sbrian log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd); 33443888Sbrian return 1; 33543888Sbrian } 33643888Sbrian 33743888Sbrian return 0; 33843888Sbrian} 33943888Sbrian 34043888Sbrianstatic int 34143888Sbrianchap_IsSet(struct descriptor *d, const fd_set *fdset) 34243888Sbrian{ 34343888Sbrian struct chap *chap = descriptor2chap(d); 34443888Sbrian 34543888Sbrian return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset); 34643888Sbrian} 34743888Sbrian 34843888Sbrianstatic void 34943888Sbrianchap_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 35043888Sbrian{ 35143888Sbrian struct chap *chap = descriptor2chap(d); 35243888Sbrian int got; 35343888Sbrian 35443888Sbrian got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len, 35543888Sbrian sizeof chap->child.buf.ptr - chap->child.buf.len - 1); 35643888Sbrian if (got == -1) { 35743888Sbrian log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno)); 35843888Sbrian chap_Cleanup(chap, SIGTERM); 35943888Sbrian } else if (got == 0) { 36043888Sbrian log_Printf(LogWARN, "Chap: Read: Child terminated connection\n"); 36143888Sbrian chap_Cleanup(chap, SIGTERM); 36243888Sbrian } else { 36343888Sbrian char *name, *key, *end; 36443888Sbrian 36543888Sbrian chap->child.buf.len += got; 36643888Sbrian chap->child.buf.ptr[chap->child.buf.len] = '\0'; 36743888Sbrian name = chap->child.buf.ptr; 36843888Sbrian name += strspn(name, " \t"); 36943888Sbrian if ((key = strchr(name, '\n')) == NULL) 37043888Sbrian end = NULL; 37143888Sbrian else 37243888Sbrian end = strchr(++key, '\n'); 37343888Sbrian 37443888Sbrian if (end == NULL) { 37543888Sbrian if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) { 37643888Sbrian log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n"); 37743888Sbrian chap_Cleanup(chap, SIGTERM); 37843888Sbrian } 37943888Sbrian } else { 38044123Sbrian#ifdef HAVE_DES 38144106Sbrian int lanman = chap->auth.physical->link.lcp.his_authtype == 0x80 && 38244106Sbrian ((chap->NTRespSent && 38344106Sbrian IsAccepted(chap->auth.physical->link.lcp.cfg.chap80lm)) || 38444106Sbrian !IsAccepted(chap->auth.physical->link.lcp.cfg.chap80nt)); 38544123Sbrian#endif 38644106Sbrian 38743888Sbrian while (end >= name && strchr(" \t\r\n", *end)) 38843888Sbrian *end-- = '\0'; 38943888Sbrian end = key - 1; 39043888Sbrian while (end >= name && strchr(" \t\r\n", *end)) 39143888Sbrian *end-- = '\0'; 39243888Sbrian key += strspn(key, " \t"); 39343888Sbrian 39444123Sbrian chap_Respond(chap, name, key, chap->auth.physical->link.lcp.his_authtype 39544123Sbrian#ifdef HAVE_DES 39644123Sbrian , lanman 39744123Sbrian#endif 39844123Sbrian ); 39943888Sbrian chap_Cleanup(chap, 0); 40043888Sbrian } 40143888Sbrian } 40243888Sbrian} 40343888Sbrian 40443888Sbrianstatic int 40543888Sbrianchap_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 40643888Sbrian{ 40743888Sbrian /* We never want to write here ! */ 40843888Sbrian log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n"); 40943888Sbrian return 0; 41043888Sbrian} 41143888Sbrian 41243888Sbrianstatic void 41343693Sbrianchap_Challenge(struct authinfo *authp) 41443693Sbrian{ 41543693Sbrian struct chap *chap = auth2chap(authp); 41613379Sphk int len, i; 4176059Samurai char *cp; 4186059Samurai 41943888Sbrian len = strlen(authp->physical->dl->bundle->cfg.auth.name); 42043401Sbrian 42143888Sbrian if (!*chap->challenge) { 42243888Sbrian randinit(); 42343888Sbrian cp = chap->challenge; 42443888Sbrian 42543313Sbrian#ifndef NORADIUS 42643888Sbrian if (*authp->physical->dl->bundle->radius.cfg.file) { 42743888Sbrian /* For radius, our challenge is 16 readable NUL terminated bytes :*/ 42843888Sbrian *cp++ = 16; 42943888Sbrian for (i = 0; i < 16; i++) 43043888Sbrian *cp++ = (random() % 10) + '0'; 43143888Sbrian } else 43243313Sbrian#endif 43343888Sbrian { 43444106Sbrian#ifdef HAVE_DES 43544106Sbrian if (authp->physical->link.lcp.want_authtype == 0x80) 43644106Sbrian *cp++ = 8; /* MS does 8 byte callenges :-/ */ 43744106Sbrian else 43844106Sbrian#endif 43944106Sbrian *cp++ = random() % (CHAPCHALLENGELEN-16) + 16; 44043888Sbrian for (i = 0; i < *chap->challenge; i++) 44143888Sbrian *cp++ = random() & 0xff; 44243888Sbrian } 44343888Sbrian memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len); 44443313Sbrian } 44543693Sbrian ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, chap->challenge, 44643888Sbrian 1 + *chap->challenge + len, NULL); 4476059Samurai} 4486059Samurai 44930715Sbrianstatic void 45043693Sbrianchap_Success(struct authinfo *authp) 4516059Samurai{ 45243693Sbrian datalink_GotAuthname(authp->physical->dl, authp->in.name); 45343693Sbrian ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, "Welcome!!", 10, NULL); 45443693Sbrian authp->physical->link.lcp.auth_ineed = 0; 45543693Sbrian if (Enabled(authp->physical->dl->bundle, OPT_UTMP)) 45643693Sbrian physical_Login(authp->physical, authp->in.name); 4576059Samurai 45843693Sbrian if (authp->physical->link.lcp.auth_iwait == 0) 45943693Sbrian /* 46043693Sbrian * Either I didn't need to authenticate, or I've already been 46143693Sbrian * told that I got the answer right. 46243693Sbrian */ 46343693Sbrian datalink_AuthOk(authp->physical->dl); 46443693Sbrian} 4656059Samurai 46643693Sbrianstatic void 46743693Sbrianchap_Failure(struct authinfo *authp) 46843693Sbrian{ 46943693Sbrian ChapOutput(authp->physical, CHAP_FAILURE, authp->id, "Invalid!!", 9, NULL); 47043693Sbrian datalink_AuthNotOk(authp->physical->dl); 47143693Sbrian} 47237926Sbrian 47344106Sbrianstatic int 47444123Sbrianchap_Cmp(u_char type, char *myans, int mylen, char *hisans, int hislen 47544123Sbrian#ifdef HAVE_DES 47644123Sbrian , int lm 47744123Sbrian#endif 47844123Sbrian ) 47944106Sbrian{ 48044106Sbrian if (mylen != hislen) 48144106Sbrian return 0; 48244123Sbrian#ifdef HAVE_DES 48344106Sbrian else if (type == 0x80) { 48444106Sbrian int off = lm ? 0 : 24; 48544106Sbrian 48644106Sbrian if (memcmp(myans + off, hisans + off, 24)) 48744106Sbrian return 0; 48844123Sbrian } 48944123Sbrian#endif 49044123Sbrian else if (memcmp(myans, hisans, mylen)) 49144106Sbrian return 0; 49244106Sbrian 49344106Sbrian return 1; 49444106Sbrian} 49544106Sbrian 49644123Sbrian#ifdef HAVE_DES 49744106Sbrianstatic int 49844106Sbrianchap_HaveAnotherGo(struct chap *chap) 49944106Sbrian{ 50044106Sbrian if (++chap->peertries < 3) { 50144106Sbrian /* Give the peer another shot */ 50244106Sbrian *chap->challenge = '\0'; 50344106Sbrian chap_Challenge(&chap->auth); 50444106Sbrian return 1; 50544106Sbrian } 50644106Sbrian 50744106Sbrian return 0; 50844106Sbrian} 50944123Sbrian#endif 51044106Sbrian 51143693Sbrianvoid 51243693Sbrianchap_Init(struct chap *chap, struct physical *p) 51343693Sbrian{ 51443888Sbrian chap->desc.type = CHAP_DESCRIPTOR; 51543888Sbrian chap->desc.UpdateSet = chap_UpdateSet; 51643888Sbrian chap->desc.IsSet = chap_IsSet; 51743888Sbrian chap->desc.Read = chap_Read; 51843888Sbrian chap->desc.Write = chap_Write; 51943888Sbrian chap->child.pid = 0; 52043888Sbrian chap->child.fd = -1; 52143693Sbrian auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure); 52243693Sbrian *chap->challenge = 0; 52344123Sbrian#ifdef HAVE_DES 52444106Sbrian chap->NTRespSent = 0; 52544106Sbrian chap->peertries = 0; 52644123Sbrian#endif 52743693Sbrian} 52829840Sbrian 52943693Sbrianvoid 53043888Sbrianchap_ReInit(struct chap *chap) 53143888Sbrian{ 53243888Sbrian chap_Cleanup(chap, SIGTERM); 53343888Sbrian} 53443888Sbrian 53543888Sbrianvoid 53643693Sbrianchap_Input(struct physical *p, struct mbuf *bp) 53743693Sbrian{ 53843693Sbrian struct chap *chap = &p->dl->chap; 53944106Sbrian char *name, *key, *ans; 54044123Sbrian int len, nlen; 54143693Sbrian u_char alen; 54244123Sbrian#ifdef HAVE_DES 54344123Sbrian int lanman; 54444123Sbrian#endif 54529840Sbrian 54645220Sbrian if (bundle_Phase(p->dl->bundle) != PHASE_NETWORK && 54745220Sbrian bundle_Phase(p->dl->bundle) != PHASE_AUTHENTICATE) { 54845220Sbrian log_Printf(LogPHASE, "Unexpected chap input - dropped !\n"); 54945220Sbrian mbuf_Free(bp); 55045220Sbrian return; 55145220Sbrian } 55245220Sbrian 55344159Sbrian if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL && 55444159Sbrian ntohs(chap->auth.in.hdr.length) == 0) 55544159Sbrian log_Printf(LogWARN, "Chap Input: Truncated header !\n"); 55643693Sbrian else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE) 55743693Sbrian log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n", 55843693Sbrian chap->auth.in.hdr.code); 55943693Sbrian else { 56043693Sbrian len = mbuf_Length(bp); 56143693Sbrian ans = NULL; 56243693Sbrian 56343693Sbrian if (chap->auth.in.hdr.code != CHAP_CHALLENGE && 56443693Sbrian chap->auth.id != chap->auth.in.hdr.id && 56543693Sbrian Enabled(p->dl->bundle, OPT_IDCHECK)) { 56643693Sbrian /* Wrong conversation dude ! */ 56743693Sbrian log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n", 56843693Sbrian chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id, 56943693Sbrian chap->auth.id); 57043693Sbrian mbuf_Free(bp); 57125630Sbrian return; 57225630Sbrian } 57343693Sbrian chap->auth.id = chap->auth.in.hdr.id; /* We respond with this id */ 57429840Sbrian 57544123Sbrian#ifdef HAVE_DES 57644106Sbrian lanman = 0; 57744123Sbrian#endif 57843693Sbrian switch (chap->auth.in.hdr.code) { 57943693Sbrian case CHAP_CHALLENGE: 58044106Sbrian bp = mbuf_Read(bp, &alen, 1); 58144106Sbrian len -= alen + 1; 58243693Sbrian if (len < 0) { 58343693Sbrian log_Printf(LogERROR, "Chap Input: Truncated challenge !\n"); 58443693Sbrian mbuf_Free(bp); 58543693Sbrian return; 58643693Sbrian } 58744106Sbrian *chap->challenge = alen; 58844106Sbrian bp = mbuf_Read(bp, chap->challenge + 1, alen); 58943693Sbrian bp = auth_ReadName(&chap->auth, bp, len); 59044123Sbrian#ifdef HAVE_DES 59144106Sbrian lanman = p->link.lcp.his_authtype == 0x80 && 59244106Sbrian ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) || 59344106Sbrian !IsAccepted(p->link.lcp.cfg.chap80nt)); 59444123Sbrian#endif 59543693Sbrian break; 59643313Sbrian 59743693Sbrian case CHAP_RESPONSE: 59843693Sbrian auth_StopTimer(&chap->auth); 59943693Sbrian bp = mbuf_Read(bp, &alen, 1); 60043693Sbrian len -= alen + 1; 60143693Sbrian if (len < 0) { 60243693Sbrian log_Printf(LogERROR, "Chap Input: Truncated response !\n"); 60343693Sbrian mbuf_Free(bp); 60443693Sbrian return; 60543693Sbrian } 60643693Sbrian if ((ans = malloc(alen + 2)) == NULL) { 60743693Sbrian log_Printf(LogERROR, "Chap Input: Out of memory !\n"); 60843693Sbrian mbuf_Free(bp); 60943693Sbrian return; 61043693Sbrian } 61143693Sbrian *ans = chap->auth.id; 61243693Sbrian bp = mbuf_Read(bp, ans + 1, alen); 61343693Sbrian ans[alen+1] = '\0'; 61443693Sbrian bp = auth_ReadName(&chap->auth, bp, len); 61544123Sbrian#ifdef HAVE_DES 61644106Sbrian lanman = alen == 49 && ans[alen] == 0; 61744123Sbrian#endif 61843693Sbrian break; 61943313Sbrian 62043693Sbrian case CHAP_SUCCESS: 62143693Sbrian case CHAP_FAILURE: 62243693Sbrian /* chap->auth.in.name is already set up at CHALLENGE time */ 62343693Sbrian if ((ans = malloc(len + 1)) == NULL) { 62443693Sbrian log_Printf(LogERROR, "Chap Input: Out of memory !\n"); 62543693Sbrian mbuf_Free(bp); 62643693Sbrian return; 62743693Sbrian } 62843693Sbrian bp = mbuf_Read(bp, ans, len); 62943693Sbrian ans[len] = '\0'; 63043693Sbrian break; 63143401Sbrian } 63236285Sbrian 63343693Sbrian switch (chap->auth.in.hdr.code) { 63443693Sbrian case CHAP_CHALLENGE: 63543693Sbrian case CHAP_RESPONSE: 63643693Sbrian if (*chap->auth.in.name) 63744106Sbrian log_Printf(LogPHASE, "Chap Input: %s (%d bytes from %s%s)\n", 63844106Sbrian chapcodes[chap->auth.in.hdr.code], alen, 63944106Sbrian chap->auth.in.name, 64044123Sbrian#ifdef HAVE_DES 64144106Sbrian lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ? 64244123Sbrian " - lanman" : 64344123Sbrian#endif 64444123Sbrian ""); 64543693Sbrian else 64644106Sbrian log_Printf(LogPHASE, "Chap Input: %s (%d bytes%s)\n", 64744106Sbrian chapcodes[chap->auth.in.hdr.code], alen, 64844123Sbrian#ifdef HAVE_DES 64944106Sbrian lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ? 65044123Sbrian " - lanman" : 65144123Sbrian#endif 65244123Sbrian ""); 65343693Sbrian break; 65436285Sbrian 65543693Sbrian case CHAP_SUCCESS: 65643693Sbrian case CHAP_FAILURE: 65743693Sbrian if (*ans) 65843693Sbrian log_Printf(LogPHASE, "Chap Input: %s (%s)\n", 65943693Sbrian chapcodes[chap->auth.in.hdr.code], ans); 66043693Sbrian else 66143693Sbrian log_Printf(LogPHASE, "Chap Input: %s\n", 66243693Sbrian chapcodes[chap->auth.in.hdr.code]); 66343693Sbrian break; 6646059Samurai } 6656059Samurai 66643693Sbrian switch (chap->auth.in.hdr.code) { 66743693Sbrian case CHAP_CHALLENGE: 66843888Sbrian if (*p->dl->bundle->cfg.auth.key == '!') 66943888Sbrian chap_StartChild(chap, p->dl->bundle->cfg.auth.key + 1, 67043888Sbrian p->dl->bundle->cfg.auth.name); 67143888Sbrian else 67244106Sbrian chap_Respond(chap, p->dl->bundle->cfg.auth.name, 67344123Sbrian p->dl->bundle->cfg.auth.key, p->link.lcp.his_authtype 67444123Sbrian#ifdef HAVE_DES 67544123Sbrian , lanman 67644123Sbrian#endif 67744123Sbrian ); 67843693Sbrian break; 6796059Samurai 68043693Sbrian case CHAP_RESPONSE: 68143693Sbrian name = chap->auth.in.name; 68243693Sbrian nlen = strlen(name); 68343693Sbrian#ifndef NORADIUS 68443693Sbrian if (*p->dl->bundle->radius.cfg.file) { 68543693Sbrian chap->challenge[*chap->challenge+1] = '\0'; 68643693Sbrian radius_Authenticate(&p->dl->bundle->radius, &chap->auth, 68743693Sbrian chap->auth.in.name, ans, chap->challenge + 1); 68843693Sbrian } else 68943693Sbrian#endif 69043693Sbrian { 69143693Sbrian key = auth_GetSecret(p->dl->bundle, name, nlen, p); 69243693Sbrian if (key) { 69344106Sbrian char *myans; 69444123Sbrian#ifdef HAVE_DES 69544106Sbrian if (lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) { 69644106Sbrian log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n"); 69744106Sbrian if (chap_HaveAnotherGo(chap)) 69844106Sbrian break; 69943693Sbrian key = NULL; 70044122Sbrian } else if (!lanman && !IsEnabled(p->link.lcp.cfg.chap80nt) && 70144122Sbrian p->link.lcp.want_authtype == 0x80) { 70244106Sbrian log_Printf(LogPHASE, "Auth failure: mschap not enabled\n"); 70344106Sbrian if (chap_HaveAnotherGo(chap)) 70444106Sbrian break; 70544106Sbrian key = NULL; 70644123Sbrian } else 70744123Sbrian#endif 70844123Sbrian { 70944106Sbrian myans = chap_BuildAnswer(name, key, chap->auth.id, 71044106Sbrian chap->challenge, 71144123Sbrian p->link.lcp.want_authtype 71244123Sbrian#ifdef HAVE_DES 71344123Sbrian , lanman 71444123Sbrian#endif 71544123Sbrian ); 71644106Sbrian if (myans == NULL) 71743693Sbrian key = NULL; 71844106Sbrian else { 71944123Sbrian if (!chap_Cmp(p->link.lcp.want_authtype, myans + 1, *myans, 72044123Sbrian ans + 1, alen 72144123Sbrian#ifdef HAVE_DES 72244123Sbrian , lanman 72344123Sbrian#endif 72444123Sbrian )) 72544106Sbrian key = NULL; 72644106Sbrian free(myans); 72744106Sbrian } 72843693Sbrian } 72943693Sbrian } 7306059Samurai 73143693Sbrian if (key) 73243693Sbrian chap_Success(&chap->auth); 73343693Sbrian else 73443693Sbrian chap_Failure(&chap->auth); 73543693Sbrian } 7366059Samurai 73743693Sbrian break; 7386059Samurai 7396059Samurai case CHAP_SUCCESS: 74043693Sbrian if (p->link.lcp.auth_iwait == PROTO_CHAP) { 74143693Sbrian p->link.lcp.auth_iwait = 0; 74243693Sbrian if (p->link.lcp.auth_ineed == 0) 74343693Sbrian /* 74443693Sbrian * We've succeeded in our ``login'' 74543693Sbrian * If we're not expecting the peer to authenticate (or he already 74643693Sbrian * has), proceed to network phase. 74743693Sbrian */ 74843693Sbrian datalink_AuthOk(p->dl); 74943693Sbrian } 75043693Sbrian break; 75143693Sbrian 7526059Samurai case CHAP_FAILURE: 75343693Sbrian datalink_AuthNotOk(p->dl); 75443693Sbrian break; 7556059Samurai } 75643693Sbrian free(ans); 7576059Samurai } 75843693Sbrian 75936285Sbrian mbuf_Free(bp); 7606059Samurai} 761