/* * chap.cc - Implementation of the CHAP Protocol * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * $Id: chap.cc,v 1.2 2004/08/09 01:13:09 bcrl Exp $ * Released under the GNU Public License. See LICENSE file for details. */ #include "kernel.h" #include "debug.h" #include "chap.h" #include "link.h" #include "config.h" #include #include "md5.h" // // // CChapProtocol::CChapProtocol() : CTimedProtocol() { DebugEnter("CChapProtocol::CChapProtocol()"); m_protocolName = "CHAP"; m_pollTimer.SetFunction(ChapRechallenge, this); m_restart_count = 3; m_parent = NULL; DebugVoidReturn; } CChapProtocol::~CChapProtocol() { DebugEnter("CChapProtocol::~CChapProtocol()"); DebugVoidReturn; } // // // void CChapProtocol::Input(CPppPacket *packet) { u8 code = packet->Pull8(); u8 ident = packet->Pull8(); /*u16 len =*/ packet->Pull16(); DebugEnter("CChapProtocol::Input()"); switch(code) { case CHAP_CODE_CHALLENGE: m_lastRcvdIdent = ident; RcvAuthChal(packet); break; case CHAP_CODE_RESPONSE: if(m_lastSentIdent != ident) break; RcvAuthRsp(packet); break; case CHAP_CODE_SUCCESS: if(m_lastRcvdIdent != ident) break; RcvAuthAck(packet); break; case CHAP_CODE_FAILURE: if(m_lastRcvdIdent != ident) break; RcvAuthNak(packet); break; } DebugVoidReturn; } // // We've been challenged by the peer for authentication // void CChapProtocol::RcvAuthChal(CPppPacket *packet) { u8 value_len; u8 value[MAX_VALUE_LEN+1]; char user[MAX_USER_LEN+1]; DebugEnter("CChapProtocol::RcvAuthChal()"); memset(value, 0, MAX_VALUE_LEN+1); memset(user, '\0', MAX_USER_LEN+1); value_len = packet->Pull8(); Log(LF_DEBUG|LF_PROTO, "Challenge length %d", value_len); packet->Pull(value, value_len); LogPacket(LF_DEBUG|LF_PROTO, value, value_len); if (packet->GetLength() > MAX_USER_LEN) { Log(LF_DEBUG|LF_PROTO, "Received User too long (%u)", packet->GetLength()); DebugVoidReturn; } packet->Pull((u8 *)user, packet->GetLength()); Log(LF_DEBUG|LF_PROTO, "User = %s", user); strcpy(m_parent->ifOptions.user, user); m_parent->ifOptions.chap_rvalue_len = value_len; memcpy(m_parent->ifOptions.chap_rvalue, value, value_len); strcpy(m_parent->ifOptions.dev_class, m_parent->m_channel->dev_class); strcpy(m_parent->ifOptions.port, m_parent->m_channel->device_name); m_parent->ifOptions.proto_id = PPP_PROTO_CHAP; m_parent->ifOptions.auth_info = m_lastRcvdIdent; m_parent->ifOptions.challenge = TRUE; m_parent->GetConfig(ChapGotConfig, this, &m_parent->ifOptions); DebugVoidReturn; } // // (m_pollTimer) // Called when it's time to rechallenge the peer for credentials // timer set-up after a successful challenge and reinitialized to // a random interval between 1 and 6 minutes, >= 10 per hour. // void ChapRechallenge(void *data) { CChapProtocol *obj = (CChapProtocol *)data; DebugEnter("ChapRechallenge()"); obj->m_restart_count = CHAP_RETRIES; obj->SndAuthChal(); DebugVoidReturn; } // // Called if we dont get a response to a challenge // void CChapProtocol::TimerExpired() { DebugEnter("CChapProtocol::TimerExpired()"); if(m_restart_count > 0) { Log(LF_NOTICE|LF_PROTO, "%s: Timeout waiting for CHAP response, resending", m_parent->m_channel->device_name); SndAuthChal(); } else { Log(LF_NOTICE|LF_PROTO, "%s: Timeout waiting for CHAP response, terminating", m_parent->m_channel->device_name); m_parent->Close(-2, "CHAP: Timeout waiting for response"); } DebugVoidReturn; } // // The daemon has responded to a hail! // void ChapGotConfig(void *obj, OptionSet_t *os) { CChapProtocol *it = (CChapProtocol *)obj; CPppPacket *pkt; DebugEnter("ChapGotConfig()"); pkt = new CPppPacket; if(pkt == NULL) { Log(LF_DEBUG|LF_PROTO, "Out of Memory (pkt)"); DebugVoidReturn; } // // // if(os->is_valid) { it->m_parent->ifOptions.Set(*os); if (os->tunnel_group_len) { /* FIXME: need to pass chap challenge/resp to l2tp */ it->Down(); it->m_parent->GotConfigTunnelGroup(os); return; } if(os->challenge == TRUE) { // // Send a response // pkt->Push((u8 *)os->user, strlen(os->user)); Log(LF_DEBUG|LF_PROTO, "user = %s", os->user); pkt->Push(os->chap_rvalue, os->chap_rvalue_len); LogPacket(LF_DEBUG|LF_PROTO, os->chap_rvalue, os->chap_rvalue_len); pkt->Push8(16); pkt->Push16(pkt->GetLength() + 4); pkt->Push8(os->auth_info); // Identifier pkt->Push8(CHAP_CODE_RESPONSE); pkt->Push16(PPP_PROTO_CHAP); it->m_parent->Output(pkt); } else { // // Send an ACK // pkt->Push16(CHAP_HEADER_LENGTH); pkt->Push8(os->auth_info); // Identifier pkt->Push8(CHAP_CODE_SUCCESS); pkt->Push16(PPP_PROTO_CHAP); it->m_parent->Output(pkt); it->m_parent->m_peerAcked = TRUE; if(it->m_parent->m_phase < PHASE_NETWORK) it->m_parent->AuthReady(); // // setup the poll timer to re-challenge the peer // occationally // it->m_pollTimer.Stop(); it->m_pollTimer.Start(100 * 600); } } else { if(os->challenge != TRUE) { // // Send a Nak and close // pkt->Push16(CHAP_HEADER_LENGTH); pkt->Push8(os->auth_info); // Identifier pkt->Push8(CHAP_CODE_FAILURE); pkt->Push16(PPP_PROTO_CHAP); it->m_parent->Output(pkt); it->m_parent->Close(-2, "CHAP: We refused remote authentication"); } } delete pkt; DebugVoidReturn; } void CChapProtocol::RcvAuthRsp(CPppPacket *packet) { u8 value_len, user_len; DebugEnter("CChapProtocol::RcvAuthRsp()"); DisableTimer(); value_len = packet->Pull8(); if (value_len > MAX_VALUE_LEN) goto out; packet->Pull((u8 *)m_parent->ifOptions.chap_rvalue, value_len); m_parent->ifOptions.chap_rvalue_len = value_len; user_len = packet->GetLength(); if (user_len > MAX_USER_LEN) goto out; packet->Pull((u8 *)m_parent->ifOptions.user, user_len); m_parent->ifOptions.user[user_len] = 0; strcpy(m_parent->ifOptions.dev_class, m_parent->m_channel->dev_class); strcpy(m_parent->ifOptions.port, m_parent->m_channel->device_name); m_parent->ifOptions.proto_id = PPP_PROTO_CHAP; m_parent->ifOptions.auth_info = m_lastSentIdent; m_parent->ifOptions.challenge = FALSE; m_parent->GetConfig(ChapGotConfig, this, &m_parent->ifOptions); out: DebugVoidReturn; } void CChapProtocol::RcvAuthAck(CPppPacket *) { DebugEnter("CChapProtocol::RcvAuthAck()"); DisableTimer(); m_parent->m_wereAcked = TRUE; if(m_parent->m_peerAcked && m_parent->m_phase < PHASE_NETWORK) m_parent->AuthReady(); DebugVoidReturn; } void CChapProtocol::RcvAuthNak(CPppPacket *) { DebugEnter("CChapProtocol::RcvAuthNak()"); m_parent->Close(-2, "Remote refused our CHAP authentication"); DebugVoidReturn; } void CChapProtocol::SndAuthChal() { CPppPacket pkt; int i; MD5_CTX md5; DebugEnter("CChapProtocol::SndAuthChal()"); if (m_restart_count) m_restart_count--; // // calculate a challenge value // for(i = 0 ; i < 16; i++) m_parent->ifOptions.chap_lvalue[i] = (random() ^ m_parent->m_policy.Get(P_AUTH)) % 256; m_parent->ifOptions.chap_lvalue_len = 16; // // Compute MD5 digest // MD5Init(&md5); MD5Update(&md5, (u8 *) m_parent->ifOptions.chap_lvalue, 16); MD5Final(&md5); memcpy((u8 *) m_parent->ifOptions.chap_lvalue, md5.digest, 16); // // Send challenge // pkt.Push(sysname); pkt.Push((u8 *)m_parent->ifOptions.chap_lvalue, m_parent->ifOptions.chap_lvalue_len); pkt.Push8(16); pkt.Push16(CHAP_HEADER_LENGTH + pkt.GetLength()); pkt.Push8(++m_lastSentIdent); pkt.Push8(CHAP_CODE_CHALLENGE); pkt.Push16(PPP_PROTO_CHAP); m_parent->Output(&pkt); EnableTimer(); DebugVoidReturn; } void CChapProtocol::Up() { DebugEnter("CChapProtocol::Up()"); m_parent->m_wereAcked = TRUE; m_parent->m_peerAcked = TRUE; if (m_parent->m_phase != PHASE_AUTHENTICATE) { DebugVoidReturn; } if ((m_parent->m_rpolicy.Get(P_AUTH) != PPP_PROTO_CHAP) && (m_parent->m_lpolicy.Get(P_AUTH) != PPP_PROTO_CHAP)) { DebugVoidReturn; } if (m_parent->m_rpolicy.Get(P_AUTH) == PPP_PROTO_CHAP) { // They want us to authenticate m_parent->m_wereAcked = FALSE; Log(LF_DEBUG|LF_PROTO, "We must authenticate with CHAP"); } if (m_parent->m_lpolicy.Get(P_AUTH) == PPP_PROTO_CHAP) { // We want them to authenticate m_parent->m_peerAcked = FALSE; Log(LF_DEBUG|LF_PROTO, "We want them to authenticate with CHAP"); SndAuthChal(); } DebugVoidReturn; } void CChapProtocol::Down() { DebugEnter("CChapProtocol::Down()"); DisableTimer(); m_pollTimer.Stop(); m_parent->LinkCancelGetConfig(); DebugVoidReturn; }