]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ldapoper.cpp
Change Windows libraries to be dynamically linked
[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, "libldap.lib")
32 # pragma comment(lib, "liblber.lib")
33 #endif
34
35 /* $ModDesc: Adds the ability to authenticate opers via LDAP */
36 /* $LinkerFlags: -lldap */
37
38 // Duplicated code, also found in cmd_oper and m_sqloper
39 static bool OneOfMatches(const char* host, const char* ip, const std::string& hostlist)
40 {
41         std::stringstream hl(hostlist);
42         std::string xhost;
43         while (hl >> xhost)
44         {
45                 if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
46                 {
47                         return true;
48                 }
49         }
50         return false;
51 }
52
53 struct RAIILDAPString
54 {
55         char *str;
56
57         RAIILDAPString(char *Str)
58                 : str(Str)
59         {
60         }
61
62         ~RAIILDAPString()
63         {
64                 ldap_memfree(str);
65         }
66
67         operator char*()
68         {
69                 return str;
70         }
71
72         operator std::string()
73         {
74                 return str;
75         }
76 };
77
78 class ModuleLDAPAuth : public Module
79 {
80         std::string base;
81         std::string ldapserver;
82         std::string username;
83         std::string password;
84         std::string attribute;
85         int searchscope;
86         LDAP *conn;
87
88         bool HandleOper(LocalUser* user, const std::string& opername, const std::string& inputpass)
89         {
90                 OperIndex::iterator it = ServerInstance->Config->oper_blocks.find(opername);
91                 if (it == ServerInstance->Config->oper_blocks.end())
92                         return false;
93
94                 ConfigTag* tag = it->second->oper_block;
95                 if (!tag)
96                         return false;
97
98                 std::string acceptedhosts = tag->getString("host");
99                 std::string hostname = user->ident + "@" + user->host;
100                 if (!OneOfMatches(hostname.c_str(), user->GetIPString(), acceptedhosts))
101                         return false;
102
103                 if (!LookupOper(opername, inputpass))
104                         return false;
105
106                 user->Oper(it->second);
107                 return true;
108         }
109
110 public:
111         ModuleLDAPAuth()
112                 : conn(NULL)
113         {
114         }
115
116         void init()
117         {
118                 Implementation eventlist[] = { I_OnRehash, I_OnPreCommand };
119                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
120                 OnRehash(NULL);
121         }
122
123         virtual ~ModuleLDAPAuth()
124         {
125                 if (conn)
126                         ldap_unbind_ext(conn, NULL, NULL);
127         }
128
129         virtual void OnRehash(User* user)
130         {
131                 ConfigTag* tag = ServerInstance->Config->ConfValue("ldapoper");
132
133                 base                    = tag->getString("baserdn");
134                 ldapserver              = tag->getString("server");
135                 std::string scope       = tag->getString("searchscope");
136                 username                = tag->getString("binddn");
137                 password                = tag->getString("bindauth");
138                 attribute               = tag->getString("attribute");
139
140                 if (scope == "base")
141                         searchscope = LDAP_SCOPE_BASE;
142                 else if (scope == "onelevel")
143                         searchscope = LDAP_SCOPE_ONELEVEL;
144                 else searchscope = LDAP_SCOPE_SUBTREE;
145
146                 Connect();
147         }
148
149         bool Connect()
150         {
151                 if (conn != NULL)
152                         ldap_unbind_ext(conn, NULL, NULL);
153                 int res, v = LDAP_VERSION3;
154                 res = ldap_initialize(&conn, ldapserver.c_str());
155                 if (res != LDAP_SUCCESS)
156                 {
157                         conn = NULL;
158                         return false;
159                 }
160
161                 res = ldap_set_option(conn, LDAP_OPT_PROTOCOL_VERSION, (void *)&v);
162                 if (res != LDAP_SUCCESS)
163                 {
164                         ldap_unbind_ext(conn, NULL, NULL);
165                         conn = NULL;
166                         return false;
167                 }
168                 return true;
169         }
170
171         ModResult OnPreCommand(std::string& command, std::vector<std::string>& parameters, LocalUser* user, bool validated, const std::string& original_line)
172         {
173                 if (validated && command == "OPER" && parameters.size() >= 2)
174                 {
175                         if (HandleOper(user, parameters[0], parameters[1]))
176                                 return MOD_RES_DENY;
177                 }
178                 return MOD_RES_PASSTHRU;
179         }
180
181         bool LookupOper(const std::string& opername, const std::string& opassword)
182         {
183                 if (conn == NULL)
184                         if (!Connect())
185                                 return false;
186
187                 int res;
188                 char* authpass = strdup(password.c_str());
189                 // bind anonymously if no bind DN and authentication are given in the config
190                 struct berval cred;
191                 cred.bv_val = authpass;
192                 cred.bv_len = password.length();
193
194                 if ((res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
195                 {
196                         if (res == LDAP_SERVER_DOWN)
197                         {
198                                 // Attempt to reconnect if the connection dropped
199                                 ServerInstance->SNO->WriteToSnoMask('a', "LDAP server has gone away - reconnecting...");
200                                 Connect();
201                                 res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
202                         }
203
204                         if (res != LDAP_SUCCESS)
205                         {
206                                 free(authpass);
207                                 ldap_unbind_ext(conn, NULL, NULL);
208                                 conn = NULL;
209                                 return false;
210                         }
211                 }
212                 free(authpass);
213
214                 LDAPMessage *msg, *entry;
215                 std::string what = attribute + "=" + opername;
216                 if ((res = ldap_search_ext_s(conn, base.c_str(), searchscope, what.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg)) != LDAP_SUCCESS)
217                 {
218                         return false;
219                 }
220                 if (ldap_count_entries(conn, msg) > 1)
221                 {
222                         ldap_msgfree(msg);
223                         return false;
224                 }
225                 if ((entry = ldap_first_entry(conn, msg)) == NULL)
226                 {
227                         ldap_msgfree(msg);
228                         return false;
229                 }
230                 authpass = strdup(opassword.c_str());
231                 cred.bv_val = authpass;
232                 cred.bv_len = opassword.length();
233                 RAIILDAPString DN(ldap_get_dn(conn, entry));
234                 if ((res = ldap_sasl_bind_s(conn, DN, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) == LDAP_SUCCESS)
235                 {
236                         free(authpass);
237                         ldap_msgfree(msg);
238                         return true;
239                 }
240                 else
241                 {
242                         free(authpass);
243                         ldap_msgfree(msg);
244                         return false;
245                 }
246         }
247
248         virtual Version GetVersion()
249         {
250                 return Version("Adds the ability to authenticate opers via LDAP", VF_VENDOR);
251         }
252
253 };
254
255 MODULE_INIT(ModuleLDAPAuth)