]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ldapoper.cpp
Merge pull request #578 from SaberUK/master+configure-compiler-detection
[user/henk/code/inspircd.git] / src / modules / extra / m_ldapoper.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
5  *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
6  *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
7  *   Copyright (C) 2007 Carsten Valdemar Munk <carsten.munk+inspircd@gmail.com>
8  *
9  * This file is part of InspIRCd.  InspIRCd is free software: you can
10  * redistribute it and/or modify it under the terms of the GNU General Public
11  * License as published by the Free Software Foundation, version 2.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22
23 #include "inspircd.h"
24 #include "users.h"
25 #include "channels.h"
26 #include "modules.h"
27
28 #include <ldap.h>
29
30 #ifdef _WIN32
31 # pragma comment(lib, "ldap.lib")
32 # pragma comment(lib, "lber.lib")
33 #endif
34
35 /* $LinkerFlags: -lldap */
36
37 struct RAIILDAPString
38 {
39         char *str;
40
41         RAIILDAPString(char *Str)
42                 : str(Str)
43         {
44         }
45
46         ~RAIILDAPString()
47         {
48                 ldap_memfree(str);
49         }
50
51         operator char*()
52         {
53                 return str;
54         }
55
56         operator std::string()
57         {
58                 return str;
59         }
60 };
61
62 class ModuleLDAPAuth : public Module
63 {
64         std::string base;
65         std::string ldapserver;
66         std::string username;
67         std::string password;
68         std::string attribute;
69         int searchscope;
70         LDAP *conn;
71
72         bool HandleOper(LocalUser* user, const std::string& opername, const std::string& inputpass)
73         {
74                 OperIndex::iterator it = ServerInstance->Config->oper_blocks.find(opername);
75                 if (it == ServerInstance->Config->oper_blocks.end())
76                         return false;
77
78                 ConfigTag* tag = it->second->oper_block;
79                 if (!tag)
80                         return false;
81
82                 std::string acceptedhosts = tag->getString("host");
83                 std::string hostname = user->ident + "@" + user->host;
84                 if (!InspIRCd::MatchMask(acceptedhosts, hostname, user->GetIPString()))
85                         return false;
86
87                 if (!LookupOper(opername, inputpass))
88                         return false;
89
90                 user->Oper(it->second);
91                 return true;
92         }
93
94 public:
95         ModuleLDAPAuth()
96                 : conn(NULL)
97         {
98         }
99
100         void init() CXX11_OVERRIDE
101         {
102                 Implementation eventlist[] = { I_OnRehash, I_OnPreCommand };
103                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
104                 OnRehash(NULL);
105         }
106
107         ~ModuleLDAPAuth()
108         {
109                 if (conn)
110                         ldap_unbind_ext(conn, NULL, NULL);
111         }
112
113         void OnRehash(User* user) CXX11_OVERRIDE
114         {
115                 ConfigTag* tag = ServerInstance->Config->ConfValue("ldapoper");
116
117                 base                    = tag->getString("baserdn");
118                 ldapserver              = tag->getString("server");
119                 std::string scope       = tag->getString("searchscope");
120                 username                = tag->getString("binddn");
121                 password                = tag->getString("bindauth");
122                 attribute               = tag->getString("attribute");
123
124                 if (scope == "base")
125                         searchscope = LDAP_SCOPE_BASE;
126                 else if (scope == "onelevel")
127                         searchscope = LDAP_SCOPE_ONELEVEL;
128                 else searchscope = LDAP_SCOPE_SUBTREE;
129
130                 Connect();
131         }
132
133         bool Connect()
134         {
135                 if (conn != NULL)
136                         ldap_unbind_ext(conn, NULL, NULL);
137                 int res, v = LDAP_VERSION3;
138                 res = ldap_initialize(&conn, ldapserver.c_str());
139                 if (res != LDAP_SUCCESS)
140                 {
141                         conn = NULL;
142                         return false;
143                 }
144
145                 res = ldap_set_option(conn, LDAP_OPT_PROTOCOL_VERSION, (void *)&v);
146                 if (res != LDAP_SUCCESS)
147                 {
148                         ldap_unbind_ext(conn, NULL, NULL);
149                         conn = NULL;
150                         return false;
151                 }
152                 return true;
153         }
154
155         ModResult OnPreCommand(std::string& command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string& original_line) CXX11_OVERRIDE
156         {
157                 if (validated && command == "OPER" && parameters.size() >= 2)
158                 {
159                         if (HandleOper(user, parameters[0], parameters[1]))
160                                 return MOD_RES_DENY;
161                 }
162                 return MOD_RES_PASSTHRU;
163         }
164
165         bool LookupOper(const std::string& opername, const std::string& opassword)
166         {
167                 if (conn == NULL)
168                         if (!Connect())
169                                 return false;
170
171                 int res;
172                 char* authpass = strdup(password.c_str());
173                 // bind anonymously if no bind DN and authentication are given in the config
174                 struct berval cred;
175                 cred.bv_val = authpass;
176                 cred.bv_len = password.length();
177
178                 if ((res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
179                 {
180                         if (res == LDAP_SERVER_DOWN)
181                         {
182                                 // Attempt to reconnect if the connection dropped
183                                 ServerInstance->SNO->WriteToSnoMask('a', "LDAP server has gone away - reconnecting...");
184                                 Connect();
185                                 res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
186                         }
187
188                         if (res != LDAP_SUCCESS)
189                         {
190                                 free(authpass);
191                                 ldap_unbind_ext(conn, NULL, NULL);
192                                 conn = NULL;
193                                 return false;
194                         }
195                 }
196                 free(authpass);
197
198                 LDAPMessage *msg, *entry;
199                 std::string what = attribute + "=" + opername;
200                 if ((res = ldap_search_ext_s(conn, base.c_str(), searchscope, what.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg)) != LDAP_SUCCESS)
201                 {
202                         return false;
203                 }
204                 if (ldap_count_entries(conn, msg) > 1)
205                 {
206                         ldap_msgfree(msg);
207                         return false;
208                 }
209                 if ((entry = ldap_first_entry(conn, msg)) == NULL)
210                 {
211                         ldap_msgfree(msg);
212                         return false;
213                 }
214                 authpass = strdup(opassword.c_str());
215                 cred.bv_val = authpass;
216                 cred.bv_len = opassword.length();
217                 RAIILDAPString DN(ldap_get_dn(conn, entry));
218                 if ((res = ldap_sasl_bind_s(conn, DN, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) == LDAP_SUCCESS)
219                 {
220                         free(authpass);
221                         ldap_msgfree(msg);
222                         return true;
223                 }
224                 else
225                 {
226                         free(authpass);
227                         ldap_msgfree(msg);
228                         return false;
229                 }
230         }
231
232         Version GetVersion() CXX11_OVERRIDE
233         {
234                 return Version("Adds the ability to authenticate opers via LDAP", VF_VENDOR);
235         }
236
237 };
238
239 MODULE_INIT(ModuleLDAPAuth)