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