chap.c revision 80763
178189Sbrian/*- 278189Sbrian * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org> 378189Sbrian * based on work by Toshiharu OHNO <tony-o@iij.ad.jp> 478189Sbrian * Internet Initiative Japan, Inc (IIJ) 578189Sbrian * All rights reserved. 66059Samurai * 778189Sbrian * Redistribution and use in source and binary forms, with or without 878189Sbrian * modification, are permitted provided that the following conditions 978189Sbrian * are met: 1078189Sbrian * 1. Redistributions of source code must retain the above copyright 1178189Sbrian * notice, this list of conditions and the following disclaimer. 1278189Sbrian * 2. Redistributions in binary form must reproduce the above copyright 1378189Sbrian * notice, this list of conditions and the following disclaimer in the 1478189Sbrian * documentation and/or other materials provided with the distribution. 156059Samurai * 1678189Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1778189Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1878189Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1978189Sbrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2078189Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2178189Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2278189Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2378189Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2478189Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2578189Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2678189Sbrian * SUCH DAMAGE. 276059Samurai * 2850479Speter * $FreeBSD: head/usr.sbin/ppp/chap.c 80763 2001-07-31 21:36:00Z brian $ 296059Samurai */ 3078189Sbrian 3143313Sbrian#include <sys/param.h> 3230715Sbrian#include <netinet/in.h> 3336285Sbrian#include <netinet/in_systm.h> 3436285Sbrian#include <netinet/ip.h> 3536285Sbrian#include <sys/un.h> 3630715Sbrian 3743888Sbrian#include <errno.h> 3843888Sbrian#include <fcntl.h> 3937192Sbrian#ifdef HAVE_DES 4036287Sbrian#include <md4.h> 4137192Sbrian#endif 4230715Sbrian#include <md5.h> 4343888Sbrian#include <paths.h> 4443888Sbrian#include <signal.h> 4549976Sbrian#include <stdio.h> 4630715Sbrian#include <stdlib.h> 4744106Sbrian#include <string.h> 4843888Sbrian#include <sys/wait.h> 4936285Sbrian#include <termios.h> 5043888Sbrian#include <unistd.h> 5129840Sbrian 5246686Sbrian#include "layer.h" 5330715Sbrian#include "mbuf.h" 5430715Sbrian#include "log.h" 5530715Sbrian#include "defs.h" 5630715Sbrian#include "timer.h" 576059Samurai#include "fsm.h" 5846686Sbrian#include "proto.h" 5936285Sbrian#include "lqr.h" 606059Samurai#include "hdlc.h" 6163484Sbrian#include "lcp.h" 626735Samurai#include "auth.h" 6336285Sbrian#include "async.h" 6436285Sbrian#include "throughput.h" 6536285Sbrian#include "descriptor.h" 6643888Sbrian#include "chap.h" 6736285Sbrian#include "iplist.h" 6836285Sbrian#include "slcompress.h" 6936285Sbrian#include "ipcp.h" 7036285Sbrian#include "filter.h" 7136285Sbrian#include "ccp.h" 7236285Sbrian#include "link.h" 7336285Sbrian#include "physical.h" 7436285Sbrian#include "mp.h" 7543313Sbrian#ifndef NORADIUS 7643313Sbrian#include "radius.h" 7743313Sbrian#endif 7836285Sbrian#include "bundle.h" 7936285Sbrian#include "chat.h" 8038174Sbrian#include "cbcp.h" 8143888Sbrian#include "command.h" 8236285Sbrian#include "datalink.h" 8337192Sbrian#ifdef HAVE_DES 8436287Sbrian#include "chap_ms.h" 8567910Sbrian#include "mppe.h" 8637192Sbrian#endif 8755253Sbrian#include "id.h" 886059Samurai 8955146Sbrianstatic const char * const chapcodes[] = { 9019866Sphk "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE" 916059Samurai}; 9243693Sbrian#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1) 936059Samurai 9430715Sbrianstatic void 9536285SbrianChapOutput(struct physical *physical, u_int code, u_int id, 9643693Sbrian const u_char *ptr, int count, const char *text) 976059Samurai{ 986059Samurai int plen; 996059Samurai struct fsmheader lh; 1006059Samurai struct mbuf *bp; 1016059Samurai 10228679Sbrian plen = sizeof(struct fsmheader) + count; 1036059Samurai lh.code = code; 1046059Samurai lh.id = id; 1056059Samurai lh.length = htons(plen); 10654912Sbrian bp = m_get(plen, MB_CHAPOUT); 10730715Sbrian memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader)); 1086059Samurai if (count) 10930715Sbrian memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count); 11036285Sbrian log_DumpBp(LogDEBUG, "ChapOutput", bp); 11137926Sbrian if (text == NULL) 11237926Sbrian log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]); 11337926Sbrian else 11437926Sbrian log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text); 11546686Sbrian link_PushPacket(&physical->link, bp, physical->dl->bundle, 11650867Sbrian LINK_QUEUES(&physical->link) - 1, PROTO_CHAP); 1176059Samurai} 1186059Samurai 11943693Sbrianstatic char * 12044123Sbrianchap_BuildAnswer(char *name, char *key, u_char id, char *challenge, u_char type 12144123Sbrian#ifdef HAVE_DES 12267910Sbrian , char *peerchallenge, char *authresponse, int lanman 12344123Sbrian#endif 12444123Sbrian ) 1256059Samurai{ 12643693Sbrian char *result, *digest; 12743693Sbrian size_t nlen, klen; 12843693Sbrian 12943693Sbrian nlen = strlen(name); 13043693Sbrian klen = strlen(key); 13143693Sbrian 13243693Sbrian#ifdef HAVE_DES 13344106Sbrian if (type == 0x80) { 13443693Sbrian char expkey[AUTHLEN << 2]; 13543693Sbrian MD4_CTX MD4context; 13643693Sbrian int f; 13743693Sbrian 13843693Sbrian if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL) 13943693Sbrian return result; 14043693Sbrian 14144106Sbrian digest = result; /* the response */ 14244106Sbrian *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */ 14344106Sbrian memcpy(digest + MS_CHAP_RESPONSE_LEN, name, nlen); 14444106Sbrian if (lanman) { 14544106Sbrian memset(digest + 24, '\0', 25); 14644106Sbrian mschap_LANMan(digest, challenge + 1, key); /* LANMan response */ 14744106Sbrian } else { 14844106Sbrian memset(digest, '\0', 25); 14944106Sbrian digest += 24; 15043693Sbrian 15144106Sbrian for (f = 0; f < klen; f++) { 15244106Sbrian expkey[2*f] = key[f]; 15344106Sbrian expkey[2*f+1] = '\0'; 15444106Sbrian } 15544106Sbrian /* 15644106Sbrian * ----------- 15744106Sbrian * expkey = | k\0e\0y\0 | 15844106Sbrian * ----------- 15944106Sbrian */ 16044106Sbrian MD4Init(&MD4context); 16144106Sbrian MD4Update(&MD4context, expkey, klen << 1); 16244106Sbrian MD4Final(digest, &MD4context); 16344106Sbrian 16444106Sbrian /* 16544106Sbrian * ---- -------- ---------------- ------- ------ 16644106Sbrian * result = | 49 | LANMan | 16 byte digest | 9 * ? | name | 16744106Sbrian * ---- -------- ---------------- ------- ------ 16844106Sbrian */ 16944106Sbrian mschap_NT(digest, challenge + 1); 17043693Sbrian } 17143693Sbrian /* 17244106Sbrian * ---- -------- ------------- ----- ------ 17344106Sbrian * | | struct MS_ChapResponse24 | | 17444106Sbrian * result = | 49 | LANMan | NT digest | 0/1 | name | 17544106Sbrian * ---- -------- ------------- ----- ------ 17644106Sbrian * where only one of LANMan & NT digest are set. 17743693Sbrian */ 17867910Sbrian } else if (type == 0x81) { 17967910Sbrian char expkey[AUTHLEN << 2]; 18067910Sbrian char pwdhash[CHAP81_HASH_LEN]; 18167910Sbrian char pwdhashhash[CHAP81_HASH_LEN]; 18267910Sbrian char *ntresponse; 18367910Sbrian int f; 18467910Sbrian 18567910Sbrian if ((result = malloc(1 + nlen + CHAP81_RESPONSE_LEN)) == NULL) 18667910Sbrian return result; 18767910Sbrian 18867910Sbrian memset(result, 0, 1 + nlen + CHAP81_RESPONSE_LEN); 18967910Sbrian 19067910Sbrian digest = result; 19167910Sbrian *digest++ = CHAP81_RESPONSE_LEN; /* value size */ 19267910Sbrian 19367910Sbrian /* Copy our challenge */ 19467910Sbrian memcpy(digest, peerchallenge + 1, CHAP81_CHALLENGE_LEN); 19567910Sbrian 19667910Sbrian /* Expand password to Unicode XXX */ 19767910Sbrian for (f = 0; f < klen; f++) { 19867910Sbrian expkey[2*f] = key[f]; 19967910Sbrian expkey[2*f+1] = '\0'; 20067910Sbrian } 20167910Sbrian 20267910Sbrian ntresponse = digest + CHAP81_NTRESPONSE_OFF; 20367910Sbrian 20467910Sbrian /* Get some needed hashes */ 20567910Sbrian NtPasswordHash(expkey, klen * 2, pwdhash); 20667910Sbrian HashNtPasswordHash(pwdhash, pwdhashhash); 20767910Sbrian 20867910Sbrian /* Generate NTRESPONSE to respond on challenge call */ 20967910Sbrian GenerateNTResponse(challenge + 1, peerchallenge + 1, name, nlen, 21067910Sbrian expkey, klen * 2, ntresponse); 21167910Sbrian 21267910Sbrian /* Generate MPPE MASTERKEY */ 21368461Sbrian GetMasterKey(pwdhashhash, ntresponse, MPPE_MasterKey); /* XXX Global ! */ 21467910Sbrian 21567910Sbrian /* Generate AUTHRESPONSE to verify on auth success */ 21667910Sbrian GenerateAuthenticatorResponse(expkey, klen * 2, ntresponse, 21767910Sbrian peerchallenge + 1, challenge + 1, name, nlen, 21867910Sbrian authresponse); 21967910Sbrian 22067910Sbrian authresponse[CHAP81_AUTHRESPONSE_LEN] = 0; 22167910Sbrian 22267910Sbrian memcpy(digest + CHAP81_RESPONSE_LEN, name, nlen); 22343693Sbrian } else 22443693Sbrian#endif 22543693Sbrian if ((result = malloc(nlen + 17)) != NULL) { 22643693Sbrian /* Normal MD5 stuff */ 22743693Sbrian MD5_CTX MD5context; 22843693Sbrian 22943693Sbrian digest = result; 23043693Sbrian *digest++ = 16; /* value size */ 23143693Sbrian 23243693Sbrian MD5Init(&MD5context); 23343693Sbrian MD5Update(&MD5context, &id, 1); 23443693Sbrian MD5Update(&MD5context, key, klen); 23543693Sbrian MD5Update(&MD5context, challenge + 1, *challenge); 23643693Sbrian MD5Final(digest, &MD5context); 23743693Sbrian 23843693Sbrian memcpy(digest + 16, name, nlen); 23943693Sbrian /* 24043693Sbrian * ---- -------- ------ 24143693Sbrian * result = | 16 | digest | name | 24243693Sbrian * ---- -------- ------ 24343693Sbrian */ 24443693Sbrian } 24543693Sbrian 24643693Sbrian return result; 24743693Sbrian} 24843693Sbrian 24943693Sbrianstatic void 25043888Sbrianchap_StartChild(struct chap *chap, char *prog, const char *name) 25143888Sbrian{ 25243888Sbrian char *argv[MAXARGS], *nargv[MAXARGS]; 25343888Sbrian int argc, fd; 25443888Sbrian int in[2], out[2]; 25547849Sbrian pid_t pid; 25643888Sbrian 25743888Sbrian if (chap->child.fd != -1) { 25843888Sbrian log_Printf(LogWARN, "Chap: %s: Program already running\n", prog); 25943888Sbrian return; 26043888Sbrian } 26143888Sbrian 26243888Sbrian if (pipe(in) == -1) { 26343888Sbrian log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno)); 26443888Sbrian return; 26543888Sbrian } 26643888Sbrian 26743888Sbrian if (pipe(out) == -1) { 26843888Sbrian log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno)); 26943888Sbrian close(in[0]); 27043888Sbrian close(in[1]); 27143888Sbrian return; 27243888Sbrian } 27343888Sbrian 27447849Sbrian pid = getpid(); 27543888Sbrian switch ((chap->child.pid = fork())) { 27643888Sbrian case -1: 27743888Sbrian log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno)); 27843888Sbrian close(in[0]); 27943888Sbrian close(in[1]); 28043888Sbrian close(out[0]); 28143888Sbrian close(out[1]); 28243888Sbrian chap->child.pid = 0; 28343888Sbrian return; 28443888Sbrian 28543888Sbrian case 0: 28643888Sbrian timer_TermService(); 28754914Sbrian 28854914Sbrian if ((argc = command_Interpret(prog, strlen(prog), argv)) <= 0) { 28954914Sbrian if (argc < 0) { 29054914Sbrian log_Printf(LogWARN, "CHAP: Invalid command syntax\n"); 29154914Sbrian _exit(255); 29254914Sbrian } 29354914Sbrian _exit(0); 29454914Sbrian } 29554914Sbrian 29643888Sbrian close(in[1]); 29743888Sbrian close(out[0]); 29849976Sbrian if (out[1] == STDIN_FILENO) 29949976Sbrian out[1] = dup(out[1]); 30043888Sbrian dup2(in[0], STDIN_FILENO); 30143888Sbrian dup2(out[1], STDOUT_FILENO); 30249976Sbrian close(STDERR_FILENO); 30349976Sbrian if (open(_PATH_DEVNULL, O_RDWR) != STDERR_FILENO) { 30443888Sbrian log_Printf(LogALERT, "Chap: Failed to open %s: %s\n", 30543888Sbrian _PATH_DEVNULL, strerror(errno)); 30643888Sbrian exit(1); 30743888Sbrian } 30849976Sbrian for (fd = getdtablesize(); fd > STDERR_FILENO; fd--) 30949976Sbrian fcntl(fd, F_SETFD, 1); 31064802Sbrian#ifndef NOSUID 31155252Sbrian setuid(ID0realuid()); 31264802Sbrian#endif 31343888Sbrian command_Expand(nargv, argc, (char const *const *)argv, 31447849Sbrian chap->auth.physical->dl->bundle, 0, pid); 31543888Sbrian execvp(nargv[0], nargv); 31649976Sbrian printf("exec() of %s failed: %s\n", nargv[0], strerror(errno)); 31749976Sbrian _exit(255); 31843888Sbrian 31943888Sbrian default: 32043888Sbrian close(in[0]); 32143888Sbrian close(out[1]); 32243888Sbrian chap->child.fd = out[0]; 32343888Sbrian chap->child.buf.len = 0; 32443888Sbrian write(in[1], chap->auth.in.name, strlen(chap->auth.in.name)); 32543888Sbrian write(in[1], "\n", 1); 32645907Sbrian write(in[1], chap->challenge.peer + 1, *chap->challenge.peer); 32743888Sbrian write(in[1], "\n", 1); 32843888Sbrian write(in[1], name, strlen(name)); 32943888Sbrian write(in[1], "\n", 1); 33043888Sbrian close(in[1]); 33143888Sbrian break; 33243888Sbrian } 33343888Sbrian} 33443888Sbrian 33543888Sbrianstatic void 33643888Sbrianchap_Cleanup(struct chap *chap, int sig) 33743888Sbrian{ 33843888Sbrian if (chap->child.pid) { 33943888Sbrian int status; 34043888Sbrian 34143888Sbrian close(chap->child.fd); 34243888Sbrian chap->child.fd = -1; 34343888Sbrian if (sig) 34443888Sbrian kill(chap->child.pid, SIGTERM); 34543888Sbrian chap->child.pid = 0; 34643888Sbrian chap->child.buf.len = 0; 34743888Sbrian 34843888Sbrian if (wait(&status) == -1) 34943888Sbrian log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno)); 35043888Sbrian else if (WIFSIGNALED(status)) 35143888Sbrian log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status)); 35243888Sbrian else if (WIFEXITED(status) && WEXITSTATUS(status)) 35343888Sbrian log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status)); 35443888Sbrian } 35545907Sbrian *chap->challenge.local = *chap->challenge.peer = '\0'; 35644123Sbrian#ifdef HAVE_DES 35744106Sbrian chap->peertries = 0; 35844123Sbrian#endif 35943888Sbrian} 36043888Sbrian 36143888Sbrianstatic void 36244123Sbrianchap_Respond(struct chap *chap, char *name, char *key, u_char type 36344123Sbrian#ifdef HAVE_DES 36444123Sbrian , int lm 36544123Sbrian#endif 36644123Sbrian ) 36743888Sbrian{ 36844106Sbrian u_char *ans; 36943888Sbrian 37045907Sbrian ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge.peer, type 37144123Sbrian#ifdef HAVE_DES 37267910Sbrian , chap->challenge.local, chap->authresponse, lm 37344123Sbrian#endif 37444123Sbrian ); 37543888Sbrian 37643888Sbrian if (ans) { 37743888Sbrian ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id, 37843888Sbrian ans, *ans + 1 + strlen(name), name); 37944123Sbrian#ifdef HAVE_DES 38044106Sbrian chap->NTRespSent = !lm; 38168461Sbrian MPPE_IsServer = 0; /* XXX Global ! */ 38244123Sbrian#endif 38343888Sbrian free(ans); 38443888Sbrian } else 38543888Sbrian ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id, 38643888Sbrian "Out of memory!", 14, NULL); 38743888Sbrian} 38843888Sbrian 38943888Sbrianstatic int 39058028Sbrianchap_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 39143888Sbrian{ 39243888Sbrian struct chap *chap = descriptor2chap(d); 39343888Sbrian 39443888Sbrian if (r && chap && chap->child.fd != -1) { 39543888Sbrian FD_SET(chap->child.fd, r); 39643888Sbrian if (*n < chap->child.fd + 1) 39743888Sbrian *n = chap->child.fd + 1; 39843888Sbrian log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd); 39943888Sbrian return 1; 40043888Sbrian } 40143888Sbrian 40243888Sbrian return 0; 40343888Sbrian} 40443888Sbrian 40543888Sbrianstatic int 40658028Sbrianchap_IsSet(struct fdescriptor *d, const fd_set *fdset) 40743888Sbrian{ 40843888Sbrian struct chap *chap = descriptor2chap(d); 40943888Sbrian 41043888Sbrian return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset); 41143888Sbrian} 41243888Sbrian 41343888Sbrianstatic void 41458028Sbrianchap_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset) 41543888Sbrian{ 41643888Sbrian struct chap *chap = descriptor2chap(d); 41743888Sbrian int got; 41843888Sbrian 41943888Sbrian got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len, 42043888Sbrian sizeof chap->child.buf.ptr - chap->child.buf.len - 1); 42143888Sbrian if (got == -1) { 42243888Sbrian log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno)); 42343888Sbrian chap_Cleanup(chap, SIGTERM); 42443888Sbrian } else if (got == 0) { 42543888Sbrian log_Printf(LogWARN, "Chap: Read: Child terminated connection\n"); 42643888Sbrian chap_Cleanup(chap, SIGTERM); 42743888Sbrian } else { 42843888Sbrian char *name, *key, *end; 42943888Sbrian 43043888Sbrian chap->child.buf.len += got; 43143888Sbrian chap->child.buf.ptr[chap->child.buf.len] = '\0'; 43243888Sbrian name = chap->child.buf.ptr; 43343888Sbrian name += strspn(name, " \t"); 43443888Sbrian if ((key = strchr(name, '\n')) == NULL) 43543888Sbrian end = NULL; 43643888Sbrian else 43743888Sbrian end = strchr(++key, '\n'); 43843888Sbrian 43943888Sbrian if (end == NULL) { 44043888Sbrian if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) { 44143888Sbrian log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n"); 44243888Sbrian chap_Cleanup(chap, SIGTERM); 44343888Sbrian } 44443888Sbrian } else { 44544123Sbrian#ifdef HAVE_DES 44644106Sbrian int lanman = chap->auth.physical->link.lcp.his_authtype == 0x80 && 44744106Sbrian ((chap->NTRespSent && 44844106Sbrian IsAccepted(chap->auth.physical->link.lcp.cfg.chap80lm)) || 44944106Sbrian !IsAccepted(chap->auth.physical->link.lcp.cfg.chap80nt)); 45044123Sbrian#endif 45144106Sbrian 45243888Sbrian while (end >= name && strchr(" \t\r\n", *end)) 45343888Sbrian *end-- = '\0'; 45443888Sbrian end = key - 1; 45543888Sbrian while (end >= name && strchr(" \t\r\n", *end)) 45643888Sbrian *end-- = '\0'; 45743888Sbrian key += strspn(key, " \t"); 45843888Sbrian 45944123Sbrian chap_Respond(chap, name, key, chap->auth.physical->link.lcp.his_authtype 46044123Sbrian#ifdef HAVE_DES 46144123Sbrian , lanman 46244123Sbrian#endif 46344123Sbrian ); 46443888Sbrian chap_Cleanup(chap, 0); 46543888Sbrian } 46643888Sbrian } 46743888Sbrian} 46843888Sbrian 46943888Sbrianstatic int 47058028Sbrianchap_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset) 47143888Sbrian{ 47243888Sbrian /* We never want to write here ! */ 47343888Sbrian log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n"); 47443888Sbrian return 0; 47543888Sbrian} 47643888Sbrian 47743888Sbrianstatic void 47867910Sbrianchap_ChallengeInit(struct authinfo *authp) 47943693Sbrian{ 48043693Sbrian struct chap *chap = auth2chap(authp); 48113379Sphk int len, i; 4826059Samurai char *cp; 4836059Samurai 48443888Sbrian len = strlen(authp->physical->dl->bundle->cfg.auth.name); 48543401Sbrian 48645907Sbrian if (!*chap->challenge.local) { 48743888Sbrian randinit(); 48845907Sbrian cp = chap->challenge.local; 48943888Sbrian 49043313Sbrian#ifndef NORADIUS 49143888Sbrian if (*authp->physical->dl->bundle->radius.cfg.file) { 49243888Sbrian /* For radius, our challenge is 16 readable NUL terminated bytes :*/ 49343888Sbrian *cp++ = 16; 49443888Sbrian for (i = 0; i < 16; i++) 49543888Sbrian *cp++ = (random() % 10) + '0'; 49643888Sbrian } else 49743313Sbrian#endif 49843888Sbrian { 49944106Sbrian#ifdef HAVE_DES 50044106Sbrian if (authp->physical->link.lcp.want_authtype == 0x80) 50144106Sbrian *cp++ = 8; /* MS does 8 byte callenges :-/ */ 50267910Sbrian else if (authp->physical->link.lcp.want_authtype == 0x81) 50367910Sbrian *cp++ = 16; /* MS-CHAP-V2 does 16 bytes challenges */ 50444106Sbrian else 50544106Sbrian#endif 50644106Sbrian *cp++ = random() % (CHAPCHALLENGELEN-16) + 16; 50745907Sbrian for (i = 0; i < *chap->challenge.local; i++) 50843888Sbrian *cp++ = random() & 0xff; 50943888Sbrian } 51043888Sbrian memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len); 51143313Sbrian } 5126059Samurai} 5136059Samurai 51430715Sbrianstatic void 51567910Sbrianchap_Challenge(struct authinfo *authp) 51667910Sbrian{ 51767910Sbrian struct chap *chap = auth2chap(authp); 51867910Sbrian int len; 51967910Sbrian 52067912Sbrian log_Printf(LogDEBUG, "CHAP%02X: Challenge\n", 52167912Sbrian authp->physical->link.lcp.want_authtype); 52267910Sbrian 52367910Sbrian len = strlen(authp->physical->dl->bundle->cfg.auth.name); 52467910Sbrian 52567910Sbrian /* Generate new local challenge value */ 52667910Sbrian if (!*chap->challenge.local) 52767910Sbrian chap_ChallengeInit(authp); 52867910Sbrian 52967910Sbrian#ifdef HAVE_DES 53067910Sbrian if (authp->physical->link.lcp.want_authtype == 0x81) 53167910Sbrian ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, 53267910Sbrian chap->challenge.local, 1 + *chap->challenge.local, NULL); 53367910Sbrian else 53467910Sbrian#endif 53567910Sbrian ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, 53667910Sbrian chap->challenge.local, 1 + *chap->challenge.local + len, NULL); 53767910Sbrian} 53867910Sbrian 53967910Sbrianstatic void 54043693Sbrianchap_Success(struct authinfo *authp) 5416059Samurai{ 54267912Sbrian const char *msg; 54343693Sbrian datalink_GotAuthname(authp->physical->dl, authp->in.name); 54467910Sbrian#ifdef HAVE_DES 54567910Sbrian if (authp->physical->link.lcp.want_authtype == 0x81) { 54667910Sbrian msg = auth2chap(authp)->authresponse; 54768461Sbrian MPPE_MasterKeyValid = 1; /* XXX Global ! */ 54867910Sbrian } else 54967910Sbrian#endif 55067910Sbrian msg = "Welcome!!"; 55167910Sbrian 55280763Sbrian ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, msg, strlen(msg), 55367910Sbrian NULL); 55467910Sbrian 55543693Sbrian authp->physical->link.lcp.auth_ineed = 0; 55643693Sbrian if (Enabled(authp->physical->dl->bundle, OPT_UTMP)) 55743693Sbrian physical_Login(authp->physical, authp->in.name); 5586059Samurai 55943693Sbrian if (authp->physical->link.lcp.auth_iwait == 0) 56043693Sbrian /* 56143693Sbrian * Either I didn't need to authenticate, or I've already been 56243693Sbrian * told that I got the answer right. 56343693Sbrian */ 56443693Sbrian datalink_AuthOk(authp->physical->dl); 56543693Sbrian} 5666059Samurai 56743693Sbrianstatic void 56843693Sbrianchap_Failure(struct authinfo *authp) 56943693Sbrian{ 57067910Sbrian#ifdef HAVE_DES 57167910Sbrian char buf[1024]; 57267910Sbrian#endif 57367912Sbrian const char *msg; 57467910Sbrian 57567910Sbrian#ifdef HAVE_DES 57667910Sbrian if (authp->physical->link.lcp.want_authtype == 0x81) { 57767912Sbrian char *ptr; 57867910Sbrian int i; 57967910Sbrian 58067912Sbrian ptr = buf; 58167912Sbrian ptr += sprintf(buf, "E=691 R=0 C="); 58267910Sbrian for (i=0; i<16; i++) 58367912Sbrian ptr += sprintf(ptr, "%02X", *(auth2chap(authp)->challenge.local+1+i)); 58467910Sbrian 58567912Sbrian sprintf(ptr, " V=3 M=Invalid!"); 58667910Sbrian msg = buf; 58767910Sbrian } else 58867910Sbrian#endif 58967910Sbrian msg = "Invalid!!"; 59067910Sbrian 59167910Sbrian ChapOutput(authp->physical, CHAP_FAILURE, authp->id, msg, strlen(msg) + 1, 59267910Sbrian NULL); 59343693Sbrian datalink_AuthNotOk(authp->physical->dl); 59443693Sbrian} 59537926Sbrian 59644106Sbrianstatic int 59744123Sbrianchap_Cmp(u_char type, char *myans, int mylen, char *hisans, int hislen 59844123Sbrian#ifdef HAVE_DES 59944123Sbrian , int lm 60044123Sbrian#endif 60144123Sbrian ) 60244106Sbrian{ 60344106Sbrian if (mylen != hislen) 60444106Sbrian return 0; 60544123Sbrian#ifdef HAVE_DES 60644106Sbrian else if (type == 0x80) { 60744106Sbrian int off = lm ? 0 : 24; 60844106Sbrian 60944106Sbrian if (memcmp(myans + off, hisans + off, 24)) 61044106Sbrian return 0; 61144123Sbrian } 61244123Sbrian#endif 61344123Sbrian else if (memcmp(myans, hisans, mylen)) 61444106Sbrian return 0; 61544106Sbrian 61644106Sbrian return 1; 61744106Sbrian} 61844106Sbrian 61944123Sbrian#ifdef HAVE_DES 62044106Sbrianstatic int 62144106Sbrianchap_HaveAnotherGo(struct chap *chap) 62244106Sbrian{ 62344106Sbrian if (++chap->peertries < 3) { 62444106Sbrian /* Give the peer another shot */ 62545907Sbrian *chap->challenge.local = '\0'; 62644106Sbrian chap_Challenge(&chap->auth); 62744106Sbrian return 1; 62844106Sbrian } 62944106Sbrian 63044106Sbrian return 0; 63144106Sbrian} 63244123Sbrian#endif 63344106Sbrian 63443693Sbrianvoid 63543693Sbrianchap_Init(struct chap *chap, struct physical *p) 63643693Sbrian{ 63743888Sbrian chap->desc.type = CHAP_DESCRIPTOR; 63843888Sbrian chap->desc.UpdateSet = chap_UpdateSet; 63943888Sbrian chap->desc.IsSet = chap_IsSet; 64043888Sbrian chap->desc.Read = chap_Read; 64143888Sbrian chap->desc.Write = chap_Write; 64243888Sbrian chap->child.pid = 0; 64343888Sbrian chap->child.fd = -1; 64443693Sbrian auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure); 64545907Sbrian *chap->challenge.local = *chap->challenge.peer = '\0'; 64644123Sbrian#ifdef HAVE_DES 64744106Sbrian chap->NTRespSent = 0; 64844106Sbrian chap->peertries = 0; 64944123Sbrian#endif 65043693Sbrian} 65129840Sbrian 65243693Sbrianvoid 65343888Sbrianchap_ReInit(struct chap *chap) 65443888Sbrian{ 65543888Sbrian chap_Cleanup(chap, SIGTERM); 65643888Sbrian} 65743888Sbrian 65846686Sbrianstruct mbuf * 65946686Sbrianchap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp) 66043693Sbrian{ 66146686Sbrian struct physical *p = link2physical(l); 66243693Sbrian struct chap *chap = &p->dl->chap; 66344106Sbrian char *name, *key, *ans; 66444123Sbrian int len, nlen; 66548817Sbrian u_char alen; 66644123Sbrian#ifdef HAVE_DES 66744123Sbrian int lanman; 66844123Sbrian#endif 66929840Sbrian 67046686Sbrian if (p == NULL) { 67146686Sbrian log_Printf(LogERROR, "chap_Input: Not a physical link - dropped\n"); 67254912Sbrian m_freem(bp); 67346686Sbrian return NULL; 67446686Sbrian } 67546686Sbrian 67646686Sbrian if (bundle_Phase(bundle) != PHASE_NETWORK && 67746686Sbrian bundle_Phase(bundle) != PHASE_AUTHENTICATE) { 67845220Sbrian log_Printf(LogPHASE, "Unexpected chap input - dropped !\n"); 67954912Sbrian m_freem(bp); 68046686Sbrian return NULL; 68145220Sbrian } 68245220Sbrian 68354912Sbrian m_settype(bp, MB_CHAPIN); 68444159Sbrian if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL && 68544159Sbrian ntohs(chap->auth.in.hdr.length) == 0) 68644159Sbrian log_Printf(LogWARN, "Chap Input: Truncated header !\n"); 68743693Sbrian else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE) 68843693Sbrian log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n", 68943693Sbrian chap->auth.in.hdr.code); 69043693Sbrian else { 69154912Sbrian len = m_length(bp); 69243693Sbrian ans = NULL; 69343693Sbrian 69443693Sbrian if (chap->auth.in.hdr.code != CHAP_CHALLENGE && 69543693Sbrian chap->auth.id != chap->auth.in.hdr.id && 69646686Sbrian Enabled(bundle, OPT_IDCHECK)) { 69743693Sbrian /* Wrong conversation dude ! */ 69843693Sbrian log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n", 69943693Sbrian chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id, 70043693Sbrian chap->auth.id); 70154912Sbrian m_freem(bp); 70246686Sbrian return NULL; 70325630Sbrian } 70443693Sbrian chap->auth.id = chap->auth.in.hdr.id; /* We respond with this id */ 70529840Sbrian 70644123Sbrian#ifdef HAVE_DES 70744106Sbrian lanman = 0; 70844123Sbrian#endif 70943693Sbrian switch (chap->auth.in.hdr.code) { 71043693Sbrian case CHAP_CHALLENGE: 71144106Sbrian bp = mbuf_Read(bp, &alen, 1); 71244106Sbrian len -= alen + 1; 71343693Sbrian if (len < 0) { 71443693Sbrian log_Printf(LogERROR, "Chap Input: Truncated challenge !\n"); 71554912Sbrian m_freem(bp); 71646686Sbrian return NULL; 71743693Sbrian } 71845907Sbrian *chap->challenge.peer = alen; 71945907Sbrian bp = mbuf_Read(bp, chap->challenge.peer + 1, alen); 72043693Sbrian bp = auth_ReadName(&chap->auth, bp, len); 72144123Sbrian#ifdef HAVE_DES 72244106Sbrian lanman = p->link.lcp.his_authtype == 0x80 && 72344106Sbrian ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) || 72444106Sbrian !IsAccepted(p->link.lcp.cfg.chap80nt)); 72567910Sbrian 72667910Sbrian /* Generate local challenge value */ 72767910Sbrian chap_ChallengeInit(&chap->auth); 72844123Sbrian#endif 72943693Sbrian break; 73043313Sbrian 73143693Sbrian case CHAP_RESPONSE: 73243693Sbrian auth_StopTimer(&chap->auth); 73343693Sbrian bp = mbuf_Read(bp, &alen, 1); 73443693Sbrian len -= alen + 1; 73543693Sbrian if (len < 0) { 73643693Sbrian log_Printf(LogERROR, "Chap Input: Truncated response !\n"); 73754912Sbrian m_freem(bp); 73846686Sbrian return NULL; 73943693Sbrian } 74043693Sbrian if ((ans = malloc(alen + 2)) == NULL) { 74143693Sbrian log_Printf(LogERROR, "Chap Input: Out of memory !\n"); 74254912Sbrian m_freem(bp); 74346686Sbrian return NULL; 74443693Sbrian } 74543693Sbrian *ans = chap->auth.id; 74643693Sbrian bp = mbuf_Read(bp, ans + 1, alen); 74780721Sbrian if (p->link.lcp.want_authtype == 0x81 && ans[alen] != '\0') { 74880721Sbrian log_Printf(LogWARN, "%s: Compensating for corrupt (Win98?) " 74980721Sbrian "CHAP81 RESPONSE\n", l->name); 75080721Sbrian ans[alen] = '\0'; 75180721Sbrian } 75243693Sbrian ans[alen+1] = '\0'; 75343693Sbrian bp = auth_ReadName(&chap->auth, bp, len); 75444123Sbrian#ifdef HAVE_DES 75567910Sbrian lanman = p->link.lcp.want_authtype == 0x80 && 75667910Sbrian alen == 49 && ans[alen] == 0; 75744123Sbrian#endif 75843693Sbrian break; 75943313Sbrian 76043693Sbrian case CHAP_SUCCESS: 76143693Sbrian case CHAP_FAILURE: 76243693Sbrian /* chap->auth.in.name is already set up at CHALLENGE time */ 76343693Sbrian if ((ans = malloc(len + 1)) == NULL) { 76443693Sbrian log_Printf(LogERROR, "Chap Input: Out of memory !\n"); 76554912Sbrian m_freem(bp); 76646686Sbrian return NULL; 76743693Sbrian } 76843693Sbrian bp = mbuf_Read(bp, ans, len); 76943693Sbrian ans[len] = '\0'; 77043693Sbrian break; 77143401Sbrian } 77236285Sbrian 77343693Sbrian switch (chap->auth.in.hdr.code) { 77443693Sbrian case CHAP_CHALLENGE: 77543693Sbrian case CHAP_RESPONSE: 77643693Sbrian if (*chap->auth.in.name) 77744106Sbrian log_Printf(LogPHASE, "Chap Input: %s (%d bytes from %s%s)\n", 77844106Sbrian chapcodes[chap->auth.in.hdr.code], alen, 77944106Sbrian chap->auth.in.name, 78044123Sbrian#ifdef HAVE_DES 78144106Sbrian lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ? 78244123Sbrian " - lanman" : 78344123Sbrian#endif 78444123Sbrian ""); 78543693Sbrian else 78644106Sbrian log_Printf(LogPHASE, "Chap Input: %s (%d bytes%s)\n", 78744106Sbrian chapcodes[chap->auth.in.hdr.code], alen, 78844123Sbrian#ifdef HAVE_DES 78944106Sbrian lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ? 79044123Sbrian " - lanman" : 79144123Sbrian#endif 79244123Sbrian ""); 79343693Sbrian break; 79436285Sbrian 79543693Sbrian case CHAP_SUCCESS: 79643693Sbrian case CHAP_FAILURE: 79743693Sbrian if (*ans) 79843693Sbrian log_Printf(LogPHASE, "Chap Input: %s (%s)\n", 79943693Sbrian chapcodes[chap->auth.in.hdr.code], ans); 80043693Sbrian else 80143693Sbrian log_Printf(LogPHASE, "Chap Input: %s\n", 80243693Sbrian chapcodes[chap->auth.in.hdr.code]); 80343693Sbrian break; 8046059Samurai } 8056059Samurai 80643693Sbrian switch (chap->auth.in.hdr.code) { 80743693Sbrian case CHAP_CHALLENGE: 80864465Sbrian if (*bundle->cfg.auth.key == '!' && bundle->cfg.auth.key[1] != '!') 80946686Sbrian chap_StartChild(chap, bundle->cfg.auth.key + 1, 81046686Sbrian bundle->cfg.auth.name); 81143888Sbrian else 81264465Sbrian chap_Respond(chap, bundle->cfg.auth.name, bundle->cfg.auth.key + 81364465Sbrian (*bundle->cfg.auth.key == '!' ? 1 : 0), 81464465Sbrian p->link.lcp.his_authtype 81544123Sbrian#ifdef HAVE_DES 81644123Sbrian , lanman 81744123Sbrian#endif 81844123Sbrian ); 81943693Sbrian break; 8206059Samurai 82143693Sbrian case CHAP_RESPONSE: 82243693Sbrian name = chap->auth.in.name; 82343693Sbrian nlen = strlen(name); 82443693Sbrian#ifndef NORADIUS 82575071Sbrian if (*bundle->radius.cfg.file) 82646686Sbrian radius_Authenticate(&bundle->radius, &chap->auth, 82775071Sbrian chap->auth.in.name, ans, alen + 1, 82875071Sbrian chap->challenge.local + 1, 82975071Sbrian *chap->challenge.local); 83075071Sbrian else 83143693Sbrian#endif 83243693Sbrian { 83346686Sbrian key = auth_GetSecret(bundle, name, nlen, p); 83443693Sbrian if (key) { 83544106Sbrian char *myans; 83644123Sbrian#ifdef HAVE_DES 83767910Sbrian if (p->link.lcp.want_authtype == 0x80 && 83867910Sbrian lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) { 83944106Sbrian log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n"); 84044106Sbrian if (chap_HaveAnotherGo(chap)) 84144106Sbrian break; 84243693Sbrian key = NULL; 84367910Sbrian } else if (p->link.lcp.want_authtype == 0x80 && 84467910Sbrian !lanman && !IsEnabled(p->link.lcp.cfg.chap80nt)) { 84544106Sbrian log_Printf(LogPHASE, "Auth failure: mschap not enabled\n"); 84644106Sbrian if (chap_HaveAnotherGo(chap)) 84744106Sbrian break; 84844106Sbrian key = NULL; 84967910Sbrian } else if (p->link.lcp.want_authtype == 0x81 && 85067910Sbrian !IsEnabled(p->link.lcp.cfg.chap81)) { 85167910Sbrian log_Printf(LogPHASE, "Auth failure: CHAP81 not enabled\n"); 85267910Sbrian key = NULL; 85344123Sbrian } else 85444123Sbrian#endif 85544123Sbrian { 85667910Sbrian#ifdef HAVE_DES 85767910Sbrian /* Get peer's challenge */ 85867910Sbrian if (p->link.lcp.want_authtype == 0x81) { 85967910Sbrian chap->challenge.peer[0] = CHAP81_CHALLENGE_LEN; 86067910Sbrian memcpy(chap->challenge.peer + 1, ans + 1, CHAP81_CHALLENGE_LEN); 86167910Sbrian } 86267910Sbrian#endif 86367910Sbrian 86444106Sbrian myans = chap_BuildAnswer(name, key, chap->auth.id, 86545907Sbrian chap->challenge.local, 86644123Sbrian p->link.lcp.want_authtype 86744123Sbrian#ifdef HAVE_DES 86867910Sbrian , chap->challenge.peer, 86968461Sbrian chap->authresponse, lanman); 87068461Sbrian MPPE_IsServer = 1; /* XXX Global ! */ 87168461Sbrian#else 87268461Sbrian ); 87344123Sbrian#endif 87444106Sbrian if (myans == NULL) 87543693Sbrian key = NULL; 87644106Sbrian else { 87744123Sbrian if (!chap_Cmp(p->link.lcp.want_authtype, myans + 1, *myans, 87844123Sbrian ans + 1, alen 87944123Sbrian#ifdef HAVE_DES 88044123Sbrian , lanman 88144123Sbrian#endif 88244123Sbrian )) 88344106Sbrian key = NULL; 88444106Sbrian free(myans); 88544106Sbrian } 88643693Sbrian } 88743693Sbrian } 8886059Samurai 88943693Sbrian if (key) 89043693Sbrian chap_Success(&chap->auth); 89143693Sbrian else 89243693Sbrian chap_Failure(&chap->auth); 89343693Sbrian } 8946059Samurai 89543693Sbrian break; 8966059Samurai 8976059Samurai case CHAP_SUCCESS: 89843693Sbrian if (p->link.lcp.auth_iwait == PROTO_CHAP) { 89943693Sbrian p->link.lcp.auth_iwait = 0; 90067910Sbrian if (p->link.lcp.auth_ineed == 0) { 90167910Sbrian#ifdef HAVE_DES 90267910Sbrian if (p->link.lcp.his_authtype == 0x81) { 90367910Sbrian if (strncmp(ans, chap->authresponse, 42)) { 90467910Sbrian datalink_AuthNotOk(p->dl); 90567912Sbrian log_Printf(LogDEBUG, "CHAP81: AuthenticatorResponse: (%s)" 90667912Sbrian " != ans: (%s)\n", chap->authresponse, ans); 90767910Sbrian 90867910Sbrian } else { 90967910Sbrian /* Successful login */ 91068461Sbrian MPPE_MasterKeyValid = 1; /* XXX Global ! */ 91167910Sbrian datalink_AuthOk(p->dl); 91267910Sbrian } 91367910Sbrian } else 91467910Sbrian#endif 91543693Sbrian /* 91643693Sbrian * We've succeeded in our ``login'' 91743693Sbrian * If we're not expecting the peer to authenticate (or he already 91843693Sbrian * has), proceed to network phase. 91943693Sbrian */ 92043693Sbrian datalink_AuthOk(p->dl); 92167910Sbrian } 92243693Sbrian } 92343693Sbrian break; 92443693Sbrian 9256059Samurai case CHAP_FAILURE: 92643693Sbrian datalink_AuthNotOk(p->dl); 92743693Sbrian break; 9286059Samurai } 92943693Sbrian free(ans); 9306059Samurai } 93143693Sbrian 93254912Sbrian m_freem(bp); 93346686Sbrian return NULL; 9346059Samurai} 935