]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/hmac.cpp
Fix memory leaks on reloadmodule and spanningtree unload while connecting servers
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / hmac.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "socket.h"
16 #include "xline.h"
17 #include "../m_hash.h"
18 #include "../ssl.h"
19 #include "socketengine.h"
20
21 #include "main.h"
22 #include "utils.h"
23 #include "treeserver.h"
24 #include "link.h"
25 #include "treesocket.h"
26 #include "resolvers.h"
27
28 /* $ModDep: 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 */
29
30 const std::string& TreeSocket::GetOurChallenge()
31 {
32         return this->ourchallenge;
33 }
34
35 void TreeSocket::SetOurChallenge(const std::string &c)
36 {
37         this->ourchallenge = c;
38 }
39
40 const std::string& TreeSocket::GetTheirChallenge()
41 {
42         return this->theirchallenge;
43 }
44
45 void TreeSocket::SetTheirChallenge(const std::string &c)
46 {
47         this->theirchallenge = c;
48 }
49
50 std::string TreeSocket::MakePass(const std::string &password, const std::string &challenge)
51 {
52         /* This is a simple (maybe a bit hacky?) HMAC algorithm, thanks to jilles for
53          * suggesting the use of HMAC to secure the password against various attacks.
54          *
55          * Note: If m_sha256.so is not loaded, we MUST fall back to plaintext with no
56          *       HMAC challenge/response.
57          */
58         Module* sha256 = ServerInstance->Modules->Find("m_sha256.so");
59         if (Utils->ChallengeResponse && sha256 && !challenge.empty())
60         {
61                 /* XXX: This is how HMAC is supposed to be done:
62                  *
63                  * sha256( (pass xor 0x5c) + sha256((pass xor 0x36) + m) )
64                  *
65                  * Note that we are encoding the hex hash, not the binary
66                  * output of the hash which is slightly different to standard.
67                  *
68                  * 5c and 36 were chosen as part of the HMAC standard, because they
69                  * flip the bits in a way likely to strengthen the function.
70                  */
71                 std::string hmac1, hmac2;
72
73                 for (size_t n = 0; n < password.length(); n++)
74                 {
75                         hmac1 += static_cast<char>(password[n] ^ 0x5C);
76                         hmac2 += static_cast<char>(password[n] ^ 0x36);
77                 }
78
79                 hmac2 += challenge;
80                 hmac2 = HashRequest(Utils->Creator, sha256, hmac2).hex();
81                 
82                 std::string hmac = hmac1 + hmac2;
83                 hmac = HashRequest(Utils->Creator, sha256, hmac).hex();
84
85                 return "HMAC-SHA256:"+ hmac;
86         }
87         else if (!challenge.empty() && !sha256)
88                 ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Not authenticating to server using SHA256/HMAC because we don't have m_sha256 loaded!");
89
90         return password;
91 }
92
93 std::string TreeSocket::RandString(unsigned int ilength)
94 {
95         char* randombuf = new char[ilength+1];
96         std::string out;
97 #ifndef WINDOWS
98         int f = open("/dev/urandom", O_RDONLY, 0);
99
100         if (f >= 0)
101         {
102                 if (read(f, randombuf, ilength) < (int)ilength)
103                         ServerInstance->Logs->Log("m_spanningtree", DEFAULT, "Entropy source has gone predictable (did not return enough data)");
104                 close(f);
105         }
106         else
107 #endif
108         {
109                 for (unsigned int i = 0; i < ilength; i++)
110                         randombuf[i] = rand();
111         }
112
113         for (unsigned int i = 0; i < ilength; i++)
114         {
115                 char randchar = static_cast<char>(0x3F + (randombuf[i] & 0x3F));
116                 out += randchar;
117         }
118
119         delete[] randombuf;
120         return out;
121 }
122
123 bool TreeSocket::ComparePass(const Link& link, const std::string &theirs)
124 {
125         this->auth_fingerprint = !link.Fingerprint.empty();
126         this->auth_challenge = !ourchallenge.empty() && !theirchallenge.empty();
127
128         std::string fp;
129         if (GetIOHook())
130         {
131                 SSLCertificateRequest req(this, Utils->Creator);
132                 if (req.cert)
133                 {
134                         fp = req.cert->GetFingerprint();
135                 }
136         }
137
138         if (auth_challenge)
139         {
140                 std::string our_hmac = MakePass(link.RecvPass, ourchallenge);
141
142                 /* Straight string compare of hashes */
143                 if (our_hmac != theirs)
144                         return false;
145         }
146         else
147         {
148                 /* Straight string compare of plaintext */
149                 if (link.RecvPass != theirs)
150                         return false;
151         }
152
153         if (auth_fingerprint)
154         {
155                 /* Require fingerprint to exist and match */
156                 if (link.Fingerprint != fp)
157                 {
158                         ServerInstance->SNO->WriteToSnoMask('l',"Invalid SSL fingerprint on link %s: need '%s' got '%s'", 
159                                 link.Name.c_str(), link.Fingerprint.c_str(), fp.c_str());
160                         SendError("Provided invalid SSL fingerprint " + fp + " - expected " + link.Fingerprint);
161                         return false;
162                 }
163         }
164         else if (!fp.empty())
165         {
166                 ServerInstance->SNO->WriteToSnoMask('l', "SSL fingerprint for link %s is %s", link.Name.c_str(), fp.c_str());
167         }
168         return true;
169 }