X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_spanningtree%2Ftreesocket1.cpp;h=acc6c7fe2f4cf9fa22d3a18b848f1f928cfda414;hb=715074dd10079a3ed846e8815290ba0d13cd4bd8;hp=dbbe865b108e83245c370cf909acd30293de9bd3;hpb=e31d17e28ee3933c44d3882fa75a26582f04016a;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_spanningtree/treesocket1.cpp b/src/modules/m_spanningtree/treesocket1.cpp index dbbe865b1..acc6c7fe2 100644 --- a/src/modules/m_spanningtree/treesocket1.cpp +++ b/src/modules/m_spanningtree/treesocket1.cpp @@ -1,3 +1,16 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2007 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + #include "configreader.h" #include "users.h" #include "channels.h" @@ -9,6 +22,7 @@ #include "wildcard.h" #include "xline.h" #include "transport.h" +#include "m_hash.h" #include "socketengine.h" #include "m_spanningtree/main.h" @@ -19,7 +33,8 @@ #include "m_spanningtree/resolvers.h" #include "m_spanningtree/handshaketimer.h" -/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */ +/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_hash.h */ + /** Because most of the I/O gubbins are encapsulated within * InspSocket, we just call the superclass constructor for @@ -31,6 +46,7 @@ TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string ho { myhost = host; this->LinkState = LISTENER; + theirchallenge = ourchallenge = ""; if (listening && Hook) InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send(); } @@ -39,6 +55,7 @@ TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string ho : InspSocket(SI, host, port, listening, maxtime, bindto), Utils(Util), Hook(HookMod) { myhost = ServerName; + theirchallenge = ourchallenge = ""; this->LinkState = CONNECTING; if (Hook) InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send(); @@ -52,21 +69,14 @@ TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, int newfd, cha : InspSocket(SI, newfd, ip), Utils(Util), Hook(HookMod) { this->LinkState = WAIT_AUTH_1; + theirchallenge = ourchallenge = ""; /* If we have a transport module hooked to the parent, hook the same module to this * socket, and set a timer waiting for handshake before we send CAPAB etc. */ if (Hook) - { InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send(); - Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(Utils->LinkBlocks[0]), this->Utils)); - } - else - { - /* Otherwise, theres no lower layer transport in plain TCP/IP, - * so just send the capabilities right now. - */ - this->SendCapabilities(); - } + + Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(Utils->LinkBlocks[0]), this->Utils, 1)); } ServerState TreeSocket::GetLinkState() @@ -85,6 +95,69 @@ TreeSocket::~TreeSocket() InspSocketUnhookRequest(this, (Module*)Utils->Creator, Hook).Send(); } +const std::string& TreeSocket::GetOurChallenge() +{ + return this->ourchallenge; +} + +void TreeSocket::SetOurChallenge(const std::string &c) +{ + this->ourchallenge = c; +} + +const std::string& TreeSocket::GetTheirChallenge() +{ + return this->theirchallenge; +} + +void TreeSocket::SetTheirChallenge(const std::string &c) +{ + this->theirchallenge = c; +} + +std::string TreeSocket::MakePass(const std::string &password, const std::string &challenge) +{ + /* This is a simple (maybe a bit hacky?) HMAC algorithm, thanks to jilles for + * suggesting the use of HMAC to secure the password against various attacks. + * + * Note: If m_sha256.so is not loaded, we MUST fall back to plaintext with no + * HMAC challenge/response. + */ + Module* sha256 = Instance->FindModule("m_sha256.so"); + if (Utils->ChallengeResponse && sha256 && !challenge.empty()) + { + /* XXX: This is how HMAC is supposed to be done: + * + * sha256( (pass xor 0x5c) + sha256((pass xor 0x36) + m) ) + * + * Note that we are encoding the hex hash, not the binary + * output of the hash which is slightly different to standard. + * + * Don't ask me why its always 0x5c and 0x36... it just is. + */ + std::string hmac1, hmac2; + + for (size_t n = 0; n < password.length(); n++) + { + hmac1 += static_cast(password[n] ^ 0x5C); + hmac2 += static_cast(password[n] ^ 0x36); + } + + HashResetRequest(Utils->Creator, sha256).Send(); + hmac2 = HashSumRequest(Utils->Creator, sha256, hmac2).Send(); + + HashResetRequest(Utils->Creator, sha256).Send(); + std::string hmac = hmac1 + hmac2 + challenge; + hmac = HashSumRequest(Utils->Creator, sha256, hmac).Send(); + + return "HMAC-SHA256:"+ hmac; + } + else if (!challenge.empty() && !sha256) + Instance->Log(DEFAULT,"Not authenticating to server using SHA256/HMAC because we don't have m_sha256 loaded!"); + + return password; +} + /** When an outbound connection finishes connecting, we receive * this event, and must send our SERVER string to the other * side. If the other side is happy, as outlined in the server @@ -106,13 +179,9 @@ bool TreeSocket::OnConnected() InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send(); this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "" : this->GetIP())+"] using transport \2"+x->Hook+"\2"); } - else - this->SendCapabilities(); + this->OutboundPass = x->SendPass; /* found who we're supposed to be connecting to, send the neccessary gubbins. */ - if (Hook) - Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(*x), this->Utils)); - else - this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+x->SendPass+" 0 :"+this->Instance->Config->ServerDesc); + Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(*x), this->Utils, 2)); return true; } } @@ -194,6 +263,30 @@ std::string TreeSocket::MyCapabilities() return capabilities; } +std::string TreeSocket::RandString(unsigned int length) +{ + char* randombuf = new char[length+1]; + std::string out; + int fd = open("/dev/urandom", O_RDONLY, 0); + + if (fd >= 0) + { + read(fd, randombuf, length); + close(fd); + } + else + { + for (unsigned int i = 0; i < length; i++) + randombuf[i] = rand(); + } + + for (unsigned int i = 0; i < length; i++) + out += static_cast((randombuf[i] & 0x7F) | 0x21); + + delete[] randombuf; + return out; +} + void TreeSocket::SendCapabilities() { irc::commasepstream modulelist(MyCapabilities()); @@ -226,7 +319,15 @@ void TreeSocket::SendCapabilities() #ifdef SUPPORT_IP6LINKS ip6support = 1; #endif - this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)+" PROTOCOL="+ConvToStr(ProtocolVersion)); + std::string extra; + /* Do we have sha256 available? If so, we send a challenge */ + if (Utils->ChallengeResponse && (Instance->FindModule("m_sha256.so"))) + { + this->SetOurChallenge(RandString(20)); + extra = " CHALLENGE=" + this->GetOurChallenge(); + } + + this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)+" PROTOCOL="+ConvToStr(ProtocolVersion)+extra); this->WriteLine("CAPAB END"); } @@ -334,6 +435,25 @@ bool TreeSocket::Capab(const std::deque ¶ms) reason = "Maximum GECOS (fullname) lengths differ or remote GECOS length not specified"; if (((this->CapKeys.find("MAXAWAY") == this->CapKeys.end()) || ((this->CapKeys.find("MAXAWAY") != this->CapKeys.end()) && (this->CapKeys.find("MAXAWAY")->second != ConvToStr(MAXAWAY))))) reason = "Maximum awaymessage lengths differ or remote awaymessage length not specified"; + + /* Challenge response, store their challenge for our password */ + std::map::iterator n = this->CapKeys.find("CHALLENGE"); + if (Utils->ChallengeResponse && (n != this->CapKeys.end()) && (Instance->FindModule("m_sha256.so"))) + { + /* Challenge-response is on now */ + this->SetTheirChallenge(n->second); + if (!this->GetOurChallenge().empty() && (this->LinkState == CONNECTING)) + { + this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(OutboundPass, this->GetTheirChallenge())+" 0 :"+this->Instance->Config->ServerDesc); + } + } + else + { + /* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */ + if (this->LinkState == CONNECTING) + this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+OutboundPass+" 0 :"+this->Instance->Config->ServerDesc); + } + if (reason.length()) { this->WriteLine("ERROR :CAPAB negotiation failed: "+reason); @@ -356,8 +476,9 @@ bool TreeSocket::Capab(const std::deque ¶ms) else if ((params[0] == "CAPABILITIES") && (params.size() == 2)) { irc::tokenstream capabs(params[1]); - std::string item = "*"; - while ((item = capabs.GetToken()) != "") + std::string item; + bool more = true; + while ((more = capabs.GetToken(item))) { /* Process each key/value pair */ std::string::size_type equals = item.rfind('='); @@ -628,6 +749,7 @@ bool TreeSocket::ForceJoin(const std::string &source, std::deque &p userrec* who = NULL; /* User we are currently checking */ std::string channel = params[0]; /* Channel name, as a string */ time_t TS = atoi(params[1].c_str()); /* Timestamp given to us for remote side */ + std::string nicklist = params[2]; bool created = false; /* Try and find the channel */ @@ -676,14 +798,13 @@ bool TreeSocket::ForceJoin(const std::string &source, std::deque &p } } /* Put the final parameter of the FJOIN into a tokenstream ready to split it */ - irc::tokenstream users(params[2]); - std::string item = "*"; + irc::tokenstream users(nicklist); + std::string item; /* Now, process every 'prefixes,nick' pair */ - while (item != "") + while (users.GetToken(item)) { /* Find next user */ - item = users.GetToken(); const char* usr = item.c_str(); /* Safety check just to make sure someones not sent us an FJOIN full of spaces * (is this even possible?) */ @@ -755,7 +876,10 @@ bool TreeSocket::ForceJoin(const std::string &source, std::deque &p /* Finally, we can actually place the user into the channel. * We're sure its right. Final answer, phone a friend. */ - chanrec::JoinUser(this->Instance, who, channel.c_str(), true, ""); + if (created) + chanrec::JoinUser(this->Instance, who, channel.c_str(), true, "", TS); + else + chanrec::JoinUser(this->Instance, who, channel.c_str(), true, ""); /* Have we already queued up MAXMODES modes with parameters * (+qaohv) ready to be sent to the server? */ @@ -837,19 +961,6 @@ bool TreeSocket::ForceJoin(const std::string &source, std::deque &p for (unsigned int f = 2; f < modectr; f++) free(mode_users[f]); } - /* if we newly created the channel, set it's TS properly. */ - if (created) - { - /* find created channel .. */ - chan = this->Instance->FindChan(channel); - if (chan) - /* w00t said this shouldnt be needed but it is. - * This isnt strictly true, as chan can be NULL - * if a nick collision has occured and therefore - * the channel was never created. - */ - chan->age = TS; - } /* All done. That wasnt so bad was it, you can wipe * the sweat from your forehead now. :-) */ @@ -859,25 +970,60 @@ bool TreeSocket::ForceJoin(const std::string &source, std::deque &p /** NICK command */ bool TreeSocket::IntroduceClient(const std::string &source, std::deque ¶ms) { - if (params.size() < 8) - return true; - if (params.size() > 8) + /** Do we have enough parameters: + * NICK age nick host dhost ident +modes ip :gecos + */ + if (params.size() != 8) { this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction ("+params[1]+"?)"); return true; } - // NICK age nick host dhost ident +modes ip :gecos - // 0 1 2 3 4 5 6 7 - time_t age = atoi(params[0].c_str()); + time_t age = atoi(params[0].c_str()); const char* tempnick = params[1].c_str(); - Instance->Log(DEBUG,"New remote client %s",tempnick); + /** Check parameters for validity before introducing the client, discovered by dmb. + * XXX: Can we make this neater? + */ + if (!age) + { + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction (Invalid TS?)"); + return true; + } + else if (params[1].length() > NICKMAX) + { + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction ("+params[1]+" > NICKMAX?)"); + return true; + } + else if (params[2].length() > 64) + { + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction ("+params[2]+" > 64?)"); + return true; + } + else if (params[3].length() > 64) + { + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction ("+params[3]+" > 64?)"); + return true; + } + else if (params[4].length() > IDENTMAX) + { + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction ("+params[4]+" > IDENTMAX?)"); + return true; + } + else if (params[7].length() > MAXGECOS) + { + this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction ("+params[7]+" > MAXGECOS?)"); + return true; + } + + /** Our client looks ok, lets introduce it now + */ + Instance->Log(DEBUG,"New remote client %s",tempnick); user_hash::iterator iter = this->Instance->clientlist->find(tempnick); if (iter != this->Instance->clientlist->end()) { - // nick collision + /* nick collision */ this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+tempnick+" :Nickname collision"); userrec::QuitUser(this->Instance, iter->second, "Nickname collision"); return true; @@ -895,9 +1041,7 @@ bool TreeSocket::IntroduceClient(const std::string &source, std::dequeregistered = REG_ALL; _new->signon = age; - /* - * we need to remove the + from the modestring, so we can do our stuff - */ + /* we need to remove the + from the modestring, so we can do our stuff */ std::string::size_type pos_after_plus = params[5].find_first_not_of('+'); if (pos_after_plus != std::string::npos) params[5] = params[5].substr(pos_after_plus); @@ -922,7 +1066,9 @@ bool TreeSocket::IntroduceClient(const std::string &source, std::dequeSetSockAddr(AF_INET, params[6].c_str(), 0); Instance->AddGlobalClone(_new); - this->Instance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s!%s@%s [%s]",_new->server,_new->nick,_new->ident,_new->host, _new->GetIPString()); + + if (!this->Instance->SilentULine(_new->server)) + this->Instance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s!%s@%s [%s]",_new->server,_new->nick,_new->ident,_new->host, _new->GetIPString()); params[7] = ":" + params[7]; Utils->DoOneToAllButSender(source,"NICK", params, source); @@ -1149,11 +1295,10 @@ void TreeSocket::SendUsers(TreeServer* Current) */ void TreeSocket::DoBurst(TreeServer* s) { + std::string name = s->GetName(); std::string burst = "BURST "+ConvToStr(Instance->Time(true)); std::string endburst = "ENDBURST"; - // Because by the end of the netburst, it could be gone! - std::string name = s->GetName(); - this->Instance->SNO->WriteToSnoMask('l',"Bursting to \2"+name+"\2."); + this->Instance->SNO->WriteToSnoMask('l',"Bursting to \2%s\2 (Authentication: %s).", name.c_str(), this->GetTheirChallenge().empty() ? "plaintext password" : "SHA256-HMAC challenge-response"); this->WriteLine(burst); /* send our version string */ this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" VERSION :"+this->Instance->GetVersionString());