| /*- |
| * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org> |
| * based on work by Toshiharu OHNO <tony-o@iij.ad.jp> |
| * Internet Initiative Japan, Inc (IIJ) |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * $FreeBSD: src/usr.sbin/ppp/chap.c,v 1.86.26.1 2010/12/21 17:10:29 kensmith Exp $ |
| */ |
| |
| #include <sys/param.h> |
| #include <netinet/in.h> |
| #include <netinet/in_systm.h> |
| #include <netinet/ip.h> |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #ifndef NODES |
| #include <md4.h> |
| #endif |
| #include <md5.h> |
| #include <paths.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/wait.h> |
| #include <termios.h> |
| #include <unistd.h> |
| |
| #include "layer.h" |
| #include "mbuf.h" |
| #include "log.h" |
| #include "defs.h" |
| #include "timer.h" |
| #include "fsm.h" |
| #include "proto.h" |
| #include "lqr.h" |
| #include "hdlc.h" |
| #include "lcp.h" |
| #include "auth.h" |
| #include "async.h" |
| #include "throughput.h" |
| #include "descriptor.h" |
| #include "chap.h" |
| #include "iplist.h" |
| #include "slcompress.h" |
| #include "ncpaddr.h" |
| #include "ipcp.h" |
| #include "filter.h" |
| #include "ccp.h" |
| #include "link.h" |
| #include "physical.h" |
| #include "mp.h" |
| #ifndef NORADIUS |
| #include "radius.h" |
| #endif |
| #include "ipv6cp.h" |
| #include "ncp.h" |
| #include "bundle.h" |
| #include "chat.h" |
| #include "cbcp.h" |
| #include "command.h" |
| #include "datalink.h" |
| #ifndef NODES |
| #include "chap_ms.h" |
| #include "mppe.h" |
| #endif |
| #include "id.h" |
| |
| static const char * const chapcodes[] = { |
| "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE" |
| }; |
| #define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1) |
| |
| static void |
| ChapOutput(struct physical *physical, u_int code, u_int id, |
| const u_char *ptr, int count, const char *text) |
| { |
| int plen; |
| struct fsmheader lh; |
| struct mbuf *bp; |
| |
| plen = sizeof(struct fsmheader) + count; |
| lh.code = code; |
| lh.id = id; |
| lh.length = htons(plen); |
| bp = m_get(plen, MB_CHAPOUT); |
| memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader)); |
| if (count) |
| memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count); |
| log_DumpBp(LogDEBUG, "ChapOutput", bp); |
| if (text == NULL) |
| log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]); |
| else |
| log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text); |
| link_PushPacket(&physical->link, bp, physical->dl->bundle, |
| LINK_QUEUES(&physical->link) - 1, PROTO_CHAP); |
| } |
| |
| static char * |
| chap_BuildAnswer(char *name, char *key, u_char id, char *challenge |
| #ifndef NODES |
| , u_char type, char *peerchallenge, char *authresponse, |
| int lanman |
| #endif |
| ) |
| { |
| char *result, *digest; |
| size_t nlen, klen; |
| |
| nlen = strlen(name); |
| klen = strlen(key); |
| |
| #ifndef NODES |
| if (type == 0x80) { |
| char expkey[AUTHLEN << 2]; |
| MD4_CTX MD4context; |
| size_t f; |
| |
| if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL) |
| return result; |
| |
| digest = result; /* the response */ |
| *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */ |
| memcpy(digest + MS_CHAP_RESPONSE_LEN, name, nlen); |
| if (lanman) { |
| memset(digest + 24, '\0', 25); |
| mschap_LANMan(digest, challenge + 1, key); /* LANMan response */ |
| } else { |
| memset(digest, '\0', 25); |
| digest += 24; |
| |
| for (f = 0; f < klen; f++) { |
| expkey[2*f] = key[f]; |
| expkey[2*f+1] = '\0'; |
| } |
| /* |
| * ----------- |
| * expkey = | k\0e\0y\0 | |
| * ----------- |
| */ |
| MD4Init(&MD4context); |
| MD4Update(&MD4context, expkey, klen << 1); |
| MD4Final(digest, &MD4context); |
| |
| /* |
| * ---- -------- ---------------- ------- ------ |
| * result = | 49 | LANMan | 16 byte digest | 9 * ? | name | |
| * ---- -------- ---------------- ------- ------ |
| */ |
| mschap_NT(digest, challenge + 1); |
| } |
| /* |
| * ---- -------- ------------- ----- ------ |
| * | | struct MS_ChapResponse24 | | |
| * result = | 49 | LANMan | NT digest | 0/1 | name | |
| * ---- -------- ------------- ----- ------ |
| * where only one of LANMan & NT digest are set. |
| */ |
| } else if (type == 0x81) { |
| char expkey[AUTHLEN << 2]; |
| char pwdhash[CHAP81_HASH_LEN]; |
| char pwdhashhash[CHAP81_HASH_LEN]; |
| char *ntresponse; |
| size_t f; |
| |
| if ((result = malloc(1 + nlen + CHAP81_RESPONSE_LEN)) == NULL) |
| return result; |
| |
| memset(result, 0, 1 + nlen + CHAP81_RESPONSE_LEN); |
| |
| digest = result; |
| *digest++ = CHAP81_RESPONSE_LEN; /* value size */ |
| |
| /* Copy our challenge */ |
| memcpy(digest, peerchallenge + 1, CHAP81_CHALLENGE_LEN); |
| |
| /* Expand password to Unicode XXX */ |
| for (f = 0; f < klen; f++) { |
| expkey[2*f] = key[f]; |
| expkey[2*f+1] = '\0'; |
| } |
| |
| ntresponse = digest + CHAP81_NTRESPONSE_OFF; |
| |
| /* Get some needed hashes */ |
| NtPasswordHash(expkey, klen * 2, pwdhash); |
| HashNtPasswordHash(pwdhash, pwdhashhash); |
| |
| /* Generate NTRESPONSE to respond on challenge call */ |
| GenerateNTResponse(challenge + 1, peerchallenge + 1, name, |
| expkey, klen * 2, ntresponse); |
| |
| /* Generate MPPE MASTERKEY */ |
| GetMasterKey(pwdhashhash, ntresponse, MPPE_MasterKey); /* XXX Global ! */ |
| |
| /* Generate AUTHRESPONSE to verify on auth success */ |
| GenerateAuthenticatorResponse(expkey, klen * 2, ntresponse, |
| peerchallenge + 1, challenge + 1, name, |
| authresponse); |
| |
| authresponse[CHAP81_AUTHRESPONSE_LEN] = 0; |
| |
| memcpy(digest + CHAP81_RESPONSE_LEN, name, nlen); |
| } else |
| #endif |
| if ((result = malloc(nlen + 17)) != NULL) { |
| /* Normal MD5 stuff */ |
| MD5_CTX MD5context; |
| |
| digest = result; |
| *digest++ = 16; /* value size */ |
| |
| MD5Init(&MD5context); |
| MD5Update(&MD5context, &id, 1); |
| MD5Update(&MD5context, key, klen); |
| MD5Update(&MD5context, challenge + 1, *challenge); |
| MD5Final(digest, &MD5context); |
| |
| memcpy(digest + 16, name, nlen); |
| /* |
| * ---- -------- ------ |
| * result = | 16 | digest | name | |
| * ---- -------- ------ |
| */ |
| } |
| |
| return result; |
| } |
| |
| static void |
| chap_StartChild(struct chap *chap, char *prog, const char *name) |
| { |
| char *argv[MAXARGS], *nargv[MAXARGS]; |
| int argc, fd; |
| int in[2], out[2]; |
| pid_t pid; |
| |
| if (chap->child.fd != -1) { |
| log_Printf(LogWARN, "Chap: %s: Program already running\n", prog); |
| return; |
| } |
| |
| if (pipe(in) == -1) { |
| log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno)); |
| return; |
| } |
| |
| if (pipe(out) == -1) { |
| log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno)); |
| close(in[0]); |
| close(in[1]); |
| return; |
| } |
| |
| pid = getpid(); |
| switch ((chap->child.pid = fork())) { |
| case -1: |
| log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno)); |
| close(in[0]); |
| close(in[1]); |
| close(out[0]); |
| close(out[1]); |
| chap->child.pid = 0; |
| return; |
| |
| case 0: |
| timer_TermService(); |
| |
| if ((argc = command_Interpret(prog, strlen(prog), argv)) <= 0) { |
| if (argc < 0) { |
| log_Printf(LogWARN, "CHAP: Invalid command syntax\n"); |
| _exit(255); |
| } |
| _exit(0); |
| } |
| |
| close(in[1]); |
| close(out[0]); |
| if (out[1] == STDIN_FILENO) |
| out[1] = dup(out[1]); |
| dup2(in[0], STDIN_FILENO); |
| dup2(out[1], STDOUT_FILENO); |
| close(STDERR_FILENO); |
| if (open(_PATH_DEVNULL, O_RDWR) != STDERR_FILENO) { |
| log_Printf(LogALERT, "Chap: Failed to open %s: %s\n", |
| _PATH_DEVNULL, strerror(errno)); |
| exit(1); |
| } |
| for (fd = getdtablesize(); fd > STDERR_FILENO; fd--) |
| fcntl(fd, F_SETFD, 1); |
| #ifndef NOSUID |
| setuid(ID0realuid()); |
| #endif |
| command_Expand(nargv, argc, (char const *const *)argv, |
| chap->auth.physical->dl->bundle, 0, pid); |
| execvp(nargv[0], nargv); |
| printf("exec() of %s failed: %s\n", nargv[0], strerror(errno)); |
| _exit(255); |
| |
| default: |
| close(in[0]); |
| close(out[1]); |
| chap->child.fd = out[0]; |
| chap->child.buf.len = 0; |
| write(in[1], chap->auth.in.name, strlen(chap->auth.in.name)); |
| write(in[1], "\n", 1); |
| write(in[1], chap->challenge.peer + 1, *chap->challenge.peer); |
| write(in[1], "\n", 1); |
| write(in[1], name, strlen(name)); |
| write(in[1], "\n", 1); |
| close(in[1]); |
| break; |
| } |
| } |
| |
| static void |
| chap_Cleanup(struct chap *chap, int sig) |
| { |
| if (chap->child.pid) { |
| int status; |
| |
| close(chap->child.fd); |
| chap->child.fd = -1; |
| if (sig) |
| kill(chap->child.pid, SIGTERM); |
| chap->child.pid = 0; |
| chap->child.buf.len = 0; |
| |
| if (wait(&status) == -1) |
| log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno)); |
| else if (WIFSIGNALED(status)) |
| log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status)); |
| else if (WIFEXITED(status) && WEXITSTATUS(status)) |
| log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status)); |
| } |
| *chap->challenge.local = *chap->challenge.peer = '\0'; |
| #ifndef NODES |
| chap->peertries = 0; |
| #endif |
| } |
| |
| static void |
| chap_Respond(struct chap *chap, char *name, char *key |
| #ifndef NODES |
| , u_char type, int lm |
| #endif |
| ) |
| { |
| u_char *ans; |
| |
| ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge.peer |
| #ifndef NODES |
| , type, chap->challenge.local, chap->authresponse, lm |
| #endif |
| ); |
| |
| if (ans) { |
| ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id, |
| ans, *ans + 1 + strlen(name), name); |
| #ifndef NODES |
| chap->NTRespSent = !lm; |
| MPPE_IsServer = 0; /* XXX Global ! */ |
| #endif |
| free(ans); |
| } else |
| ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id, |
| "Out of memory!", 14, NULL); |
| } |
| |
| static int |
| chap_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w __unused, |
| fd_set *e __unused, int *n) |
| { |
| struct chap *chap = descriptor2chap(d); |
| |
| if (r && chap && chap->child.fd != -1) { |
| FD_SET(chap->child.fd, r); |
| if (*n < chap->child.fd + 1) |
| *n = chap->child.fd + 1; |
| log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| chap_IsSet(struct fdescriptor *d, const fd_set *fdset) |
| { |
| struct chap *chap = descriptor2chap(d); |
| |
| return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset); |
| } |
| |
| static void |
| chap_Read(struct fdescriptor *d, struct bundle *bundle __unused, |
| const fd_set *fdset __unused) |
| { |
| struct chap *chap = descriptor2chap(d); |
| int got; |
| |
| got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len, |
| sizeof chap->child.buf.ptr - chap->child.buf.len - 1); |
| if (got == -1) { |
| log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno)); |
| chap_Cleanup(chap, SIGTERM); |
| } else if (got == 0) { |
| log_Printf(LogWARN, "Chap: Read: Child terminated connection\n"); |
| chap_Cleanup(chap, SIGTERM); |
| } else { |
| char *name, *key, *end; |
| |
| chap->child.buf.len += got; |
| chap->child.buf.ptr[chap->child.buf.len] = '\0'; |
| name = chap->child.buf.ptr; |
| name += strspn(name, " \t"); |
| if ((key = strchr(name, '\n')) == NULL) |
| end = NULL; |
| else |
| end = strchr(++key, '\n'); |
| |
| if (end == NULL) { |
| if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) { |
| log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n"); |
| chap_Cleanup(chap, SIGTERM); |
| } |
| } else { |
| #ifndef NODES |
| int lanman = chap->auth.physical->link.lcp.his_authtype == 0x80 && |
| ((chap->NTRespSent && |
| IsAccepted(chap->auth.physical->link.lcp.cfg.chap80lm)) || |
| !IsAccepted(chap->auth.physical->link.lcp.cfg.chap80nt)); |
| #endif |
| |
| while (end >= name && strchr(" \t\r\n", *end)) |
| *end-- = '\0'; |
| end = key - 1; |
| while (end >= name && strchr(" \t\r\n", *end)) |
| *end-- = '\0'; |
| key += strspn(key, " \t"); |
| |
| chap_Respond(chap, name, key |
| #ifndef NODES |
| , chap->auth.physical->link.lcp.his_authtype, lanman |
| #endif |
| ); |
| chap_Cleanup(chap, 0); |
| } |
| } |
| } |
| |
| static int |
| chap_Write(struct fdescriptor *d __unused, struct bundle *bundle __unused, |
| const fd_set *fdset __unused) |
| { |
| /* We never want to write here ! */ |
| log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n"); |
| return 0; |
| } |
| |
| static void |
| chap_ChallengeInit(struct authinfo *authp) |
| { |
| struct chap *chap = auth2chap(authp); |
| int len, i; |
| char *cp; |
| |
| len = strlen(authp->physical->dl->bundle->cfg.auth.name); |
| |
| if (!*chap->challenge.local) { |
| randinit(); |
| cp = chap->challenge.local; |
| |
| #ifndef NORADIUS |
| if (*authp->physical->dl->bundle->radius.cfg.file) { |
| /* For radius, our challenge is 16 readable NUL terminated bytes :*/ |
| *cp++ = 16; |
| for (i = 0; i < 16; i++) |
| *cp++ = (random() % 10) + '0'; |
| } else |
| #endif |
| { |
| #ifndef NODES |
| if (authp->physical->link.lcp.want_authtype == 0x80) |
| *cp++ = 8; /* MS does 8 byte callenges :-/ */ |
| else if (authp->physical->link.lcp.want_authtype == 0x81) |
| *cp++ = 16; /* MS-CHAP-V2 does 16 bytes challenges */ |
| else |
| #endif |
| *cp++ = random() % (CHAPCHALLENGELEN-16) + 16; |
| for (i = 0; i < *chap->challenge.local; i++) |
| *cp++ = random() & 0xff; |
| } |
| memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len); |
| } |
| } |
| |
| static void |
| chap_Challenge(struct authinfo *authp) |
| { |
| struct chap *chap = auth2chap(authp); |
| int len; |
| |
| log_Printf(LogDEBUG, "CHAP%02X: Challenge\n", |
| authp->physical->link.lcp.want_authtype); |
| |
| len = strlen(authp->physical->dl->bundle->cfg.auth.name); |
| |
| /* Generate new local challenge value */ |
| if (!*chap->challenge.local) |
| chap_ChallengeInit(authp); |
| |
| #ifndef NODES |
| if (authp->physical->link.lcp.want_authtype == 0x81) |
| ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, |
| chap->challenge.local, 1 + *chap->challenge.local, NULL); |
| else |
| #endif |
| ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, |
| chap->challenge.local, 1 + *chap->challenge.local + len, NULL); |
| } |
| |
| static void |
| chap_Success(struct authinfo *authp) |
| { |
| struct bundle *bundle = authp->physical->dl->bundle; |
| const char *msg; |
| |
| datalink_GotAuthname(authp->physical->dl, authp->in.name); |
| #ifndef NODES |
| if (authp->physical->link.lcp.want_authtype == 0x81) { |
| #ifndef NORADIUS |
| if (*bundle->radius.cfg.file && bundle->radius.msrepstr) |
| msg = bundle->radius.msrepstr; |
| else |
| #endif |
| msg = auth2chap(authp)->authresponse; |
| MPPE_MasterKeyValid = 1; /* XXX Global ! */ |
| } else |
| #endif |
| #ifndef NORADIUS |
| if (*bundle->radius.cfg.file && bundle->radius.repstr) |
| msg = bundle->radius.repstr; |
| else |
| #endif |
| msg = "Welcome!!"; |
| |
| ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, msg, strlen(msg), |
| NULL); |
| |
| authp->physical->link.lcp.auth_ineed = 0; |
| if (Enabled(bundle, OPT_UTMP)) |
| physical_Login(authp->physical, authp->in.name); |
| |
| if (authp->physical->link.lcp.auth_iwait == 0) |
| /* |
| * Either I didn't need to authenticate, or I've already been |
| * told that I got the answer right. |
| */ |
| datalink_AuthOk(authp->physical->dl); |
| } |
| |
| static void |
| chap_Failure(struct authinfo *authp) |
| { |
| #ifndef NODES |
| char buf[1024], *ptr; |
| #endif |
| const char *msg; |
| |
| #ifndef NORADIUS |
| struct bundle *bundle = authp->physical->link.lcp.fsm.bundle; |
| if (*bundle->radius.cfg.file && bundle->radius.errstr) |
| msg = bundle->radius.errstr; |
| else |
| #endif |
| #ifndef NODES |
| if (authp->physical->link.lcp.want_authtype == 0x80) { |
| sprintf(buf, "E=691 R=1 M=Invalid!"); |
| msg = buf; |
| } else if (authp->physical->link.lcp.want_authtype == 0x81) { |
| int i; |
| |
| ptr = buf; |
| ptr += sprintf(buf, "E=691 R=0 C="); |
| for (i=0; i<16; i++) |
| ptr += sprintf(ptr, "%02X", *(auth2chap(authp)->challenge.local+1+i)); |
| |
| sprintf(ptr, " V=3 M=Invalid!"); |
| msg = buf; |
| } else |
| #endif |
| msg = "Invalid!!"; |
| |
| ChapOutput(authp->physical, CHAP_FAILURE, authp->id, msg, strlen(msg) + 1, |
| NULL); |
| datalink_AuthNotOk(authp->physical->dl); |
| } |
| |
| static int |
| chap_Cmp(char *myans, int mylen, char *hisans, int hislen |
| #ifndef NODES |
| , u_char type, int lm |
| #endif |
| ) |
| { |
| int off; |
| |
| if (mylen != hislen) |
| return 0; |
| |
| off = 0; |
| |
| #ifndef NODES |
| if (type == 0x80) { |
| off = lm ? 0 : 24; |
| mylen = 24; |
| } |
| #endif |
| |
| for (; mylen; off++, mylen--) |
| if (toupper(myans[off]) != toupper(hisans[off])) |
| return 0; |
| |
| return 1; |
| } |
| |
| #ifndef NODES |
| static int |
| chap_HaveAnotherGo(struct chap *chap) |
| { |
| if (++chap->peertries < 3) { |
| /* Give the peer another shot */ |
| *chap->challenge.local = '\0'; |
| chap_Challenge(&chap->auth); |
| return 1; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| void |
| chap_Init(struct chap *chap, struct physical *p) |
| { |
| chap->desc.type = CHAP_DESCRIPTOR; |
| chap->desc.UpdateSet = chap_UpdateSet; |
| chap->desc.IsSet = chap_IsSet; |
| chap->desc.Read = chap_Read; |
| chap->desc.Write = chap_Write; |
| chap->child.pid = 0; |
| chap->child.fd = -1; |
| auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure); |
| *chap->challenge.local = *chap->challenge.peer = '\0'; |
| #ifndef NODES |
| chap->NTRespSent = 0; |
| chap->peertries = 0; |
| #endif |
| } |
| |
| void |
| chap_ReInit(struct chap *chap) |
| { |
| chap_Cleanup(chap, SIGTERM); |
| } |
| |
| struct mbuf * |
| chap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp) |
| { |
| struct physical *p = link2physical(l); |
| struct chap *chap = &p->dl->chap; |
| char *name, *key, *ans; |
| int len; |
| size_t nlen; |
| u_char alen; |
| #ifndef NODES |
| int lanman; |
| #endif |
| |
| if (p == NULL) { |
| log_Printf(LogERROR, "chap_Input: Not a physical link - dropped\n"); |
| m_freem(bp); |
| return NULL; |
| } |
| |
| if (bundle_Phase(bundle) != PHASE_NETWORK && |
| bundle_Phase(bundle) != PHASE_AUTHENTICATE) { |
| log_Printf(LogPHASE, "Unexpected chap input - dropped !\n"); |
| m_freem(bp); |
| return NULL; |
| } |
| |
| m_settype(bp, MB_CHAPIN); |
| if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL && |
| ntohs(chap->auth.in.hdr.length) == 0) |
| log_Printf(LogWARN, "Chap Input: Truncated header !\n"); |
| else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE) |
| log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n", |
| chap->auth.in.hdr.code); |
| else { |
| len = m_length(bp); |
| ans = NULL; |
| |
| if (chap->auth.in.hdr.code != CHAP_CHALLENGE && |
| chap->auth.id != chap->auth.in.hdr.id && |
| Enabled(bundle, OPT_IDCHECK)) { |
| /* Wrong conversation dude ! */ |
| log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n", |
| chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id, |
| chap->auth.id); |
| m_freem(bp); |
| return NULL; |
| } |
| chap->auth.id = chap->auth.in.hdr.id; /* We respond with this id */ |
| |
| #ifndef NODES |
| lanman = 0; |
| #endif |
| switch (chap->auth.in.hdr.code) { |
| case CHAP_CHALLENGE: |
| bp = mbuf_Read(bp, &alen, 1); |
| len -= alen + 1; |
| if (len < 0) { |
| log_Printf(LogERROR, "Chap Input: Truncated challenge !\n"); |
| m_freem(bp); |
| return NULL; |
| } |
| *chap->challenge.peer = alen; |
| bp = mbuf_Read(bp, chap->challenge.peer + 1, alen); |
| bp = auth_ReadName(&chap->auth, bp, len); |
| #ifndef NODES |
| lanman = p->link.lcp.his_authtype == 0x80 && |
| ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) || |
| !IsAccepted(p->link.lcp.cfg.chap80nt)); |
| |
| /* Generate local challenge value */ |
| chap_ChallengeInit(&chap->auth); |
| #endif |
| break; |
| |
| case CHAP_RESPONSE: |
| auth_StopTimer(&chap->auth); |
| bp = mbuf_Read(bp, &alen, 1); |
| len -= alen + 1; |
| if (len < 0) { |
| log_Printf(LogERROR, "Chap Input: Truncated response !\n"); |
| m_freem(bp); |
| return NULL; |
| } |
| if ((ans = malloc(alen + 1)) == NULL) { |
| log_Printf(LogERROR, "Chap Input: Out of memory !\n"); |
| m_freem(bp); |
| return NULL; |
| } |
| *ans = chap->auth.id; |
| bp = mbuf_Read(bp, ans + 1, alen); |
| bp = auth_ReadName(&chap->auth, bp, len); |
| #ifndef NODES |
| lanman = p->link.lcp.want_authtype == 0x80 && |
| alen == 49 && ans[alen] == 0; |
| #endif |
| break; |
| |
| case CHAP_SUCCESS: |
| case CHAP_FAILURE: |
| /* chap->auth.in.name is already set up at CHALLENGE time */ |
| if ((ans = malloc(len + 1)) == NULL) { |
| log_Printf(LogERROR, "Chap Input: Out of memory !\n"); |
| m_freem(bp); |
| return NULL; |
| } |
| bp = mbuf_Read(bp, ans, len); |
| ans[len] = '\0'; |
| break; |
| } |
| |
| switch (chap->auth.in.hdr.code) { |
| case CHAP_CHALLENGE: |
| case CHAP_RESPONSE: |
| if (*chap->auth.in.name) |
| log_Printf(LogPHASE, "Chap Input: %s (%d bytes from %s%s)\n", |
| chapcodes[chap->auth.in.hdr.code], alen, |
| chap->auth.in.name, |
| #ifndef NODES |
| lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ? |
| " - lanman" : |
| #endif |
| ""); |
| else |
| log_Printf(LogPHASE, "Chap Input: %s (%d bytes%s)\n", |
| chapcodes[chap->auth.in.hdr.code], alen, |
| #ifndef NODES |
| lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ? |
| " - lanman" : |
| #endif |
| ""); |
| break; |
| |
| case CHAP_SUCCESS: |
| case CHAP_FAILURE: |
| if (*ans) |
| log_Printf(LogPHASE, "Chap Input: %s (%s)\n", |
| chapcodes[chap->auth.in.hdr.code], ans); |
| else |
| log_Printf(LogPHASE, "Chap Input: %s\n", |
| chapcodes[chap->auth.in.hdr.code]); |
| break; |
| } |
| |
| switch (chap->auth.in.hdr.code) { |
| case CHAP_CHALLENGE: |
| if (*bundle->cfg.auth.key == '!' && bundle->cfg.auth.key[1] != '!') |
| chap_StartChild(chap, bundle->cfg.auth.key + 1, |
| bundle->cfg.auth.name); |
| else |
| chap_Respond(chap, bundle->cfg.auth.name, bundle->cfg.auth.key + |
| (*bundle->cfg.auth.key == '!' ? 1 : 0) |
| |
| #ifndef NODES |
| , p->link.lcp.his_authtype, lanman |
| #endif |
| ); |
| break; |
| |
| case CHAP_RESPONSE: |
| name = chap->auth.in.name; |
| nlen = strlen(name); |
| #ifndef NODES |
| if (p->link.lcp.want_authtype == 0x81) { |
| struct MSCHAPv2_resp *resp = (struct MSCHAPv2_resp *)(ans + 1); |
| |
| chap->challenge.peer[0] = sizeof resp->PeerChallenge; |
| memcpy(chap->challenge.peer + 1, resp->PeerChallenge, |
| sizeof resp->PeerChallenge); |
| } |
| #endif |
| |
| #ifndef NORADIUS |
| if (*bundle->radius.cfg.file) { |
| if (!radius_Authenticate(&bundle->radius, &chap->auth, |
| chap->auth.in.name, ans, alen + 1, |
| chap->challenge.local + 1, |
| *chap->challenge.local)) |
| chap_Failure(&chap->auth); |
| } else |
| #endif |
| { |
| if (p->link.lcp.want_authtype == 0x81 && ans[alen] != '\0' && |
| alen == sizeof(struct MSCHAPv2_resp)) { |
| struct MSCHAPv2_resp *resp = (struct MSCHAPv2_resp *)(ans + 1); |
| |
| log_Printf(LogWARN, "%s: Compensating for corrupt (Win98/WinME?) " |
| "CHAP81 RESPONSE\n", l->name); |
| resp->Flags = '\0'; /* rfc2759 says it *MUST* be zero */ |
| } |
| key = auth_GetSecret(name, nlen); |
| if (key) { |
| #ifndef NODES |
| if (p->link.lcp.want_authtype == 0x80 && |
| lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) { |
| log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n"); |
| if (chap_HaveAnotherGo(chap)) |
| break; |
| key = NULL; |
| } else if (p->link.lcp.want_authtype == 0x80 && |
| !lanman && !IsEnabled(p->link.lcp.cfg.chap80nt)) { |
| log_Printf(LogPHASE, "Auth failure: mschap not enabled\n"); |
| if (chap_HaveAnotherGo(chap)) |
| break; |
| key = NULL; |
| } else if (p->link.lcp.want_authtype == 0x81 && |
| !IsEnabled(p->link.lcp.cfg.chap81)) { |
| log_Printf(LogPHASE, "Auth failure: CHAP81 not enabled\n"); |
| key = NULL; |
| } else |
| #endif |
| { |
| char *myans = chap_BuildAnswer(name, key, chap->auth.id, |
| chap->challenge.local |
| #ifndef NODES |
| , p->link.lcp.want_authtype, |
| chap->challenge.peer, |
| chap->authresponse, lanman); |
| MPPE_IsServer = 1; /* XXX Global ! */ |
| #else |
| ); |
| #endif |
| if (myans == NULL) |
| key = NULL; |
| else { |
| if (!chap_Cmp(myans + 1, *myans, ans + 1, alen |
| #ifndef NODES |
| , p->link.lcp.want_authtype, lanman |
| #endif |
| )) |
| key = NULL; |
| free(myans); |
| } |
| } |
| } |
| |
| if (key) |
| chap_Success(&chap->auth); |
| else |
| chap_Failure(&chap->auth); |
| } |
| |
| break; |
| |
| case CHAP_SUCCESS: |
| if (p->link.lcp.auth_iwait == PROTO_CHAP) { |
| p->link.lcp.auth_iwait = 0; |
| if (p->link.lcp.auth_ineed == 0) { |
| #ifndef NODES |
| if (p->link.lcp.his_authtype == 0x81) { |
| if (strncasecmp(ans, chap->authresponse, 42)) { |
| datalink_AuthNotOk(p->dl); |
| log_Printf(LogWARN, "CHAP81: AuthenticatorResponse: (%.42s)" |
| " != ans: (%.42s)\n", chap->authresponse, ans); |
| |
| } else { |
| /* Successful login */ |
| MPPE_MasterKeyValid = 1; /* XXX Global ! */ |
| datalink_AuthOk(p->dl); |
| } |
| } else |
| #endif |
| /* |
| * We've succeeded in our ``login'' |
| * If we're not expecting the peer to authenticate (or he already |
| * has), proceed to network phase. |
| */ |
| datalink_AuthOk(p->dl); |
| } |
| } |
| break; |
| |
| case CHAP_FAILURE: |
| datalink_AuthNotOk(p->dl); |
| break; |
| } |
| free(ans); |
| } |
| |
| m_freem(bp); |
| return NULL; |
| } |