]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ldapauth.cpp
Allow STARTTLS to be disabled via <gnutls starttls="no">
[user/henk/code/inspircd.git] / src / modules / extra / m_ldapauth.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2010 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  * Taken from the UnrealIRCd 4.0 SVN version, based on
14  * InspIRCd 1.1.x.
15  *
16  * UnrealIRCd 4.0 (C) 2007 Carsten Valdemar Munk
17  * This program is free but copyrighted software; see
18  *          the file COPYING for details.
19  *
20  * ---------------------------------------------------
21  * Heavily based on SQLauth
22  */
23
24 #include "inspircd.h"
25 #include "users.h"
26 #include "channels.h"
27 #include "modules.h"
28
29 #include <ldap.h>
30
31 /* $ModDesc: Allow/Deny connections based upon answer from LDAP server */
32 /* $LinkerFlags: -lldap */
33
34 class ModuleLDAPAuth : public Module
35 {
36         LocalIntExt ldapAuthed;
37         std::string base;
38         std::string attribute;
39         std::string ldapserver;
40         std::string allowpattern;
41         std::string killreason;
42         std::string username;
43         std::string password;
44         int searchscope;
45         bool verbose;
46         bool useusername;
47         LDAP *conn;
48
49 public:
50         ModuleLDAPAuth() : ldapAuthed("ldapauth", this)
51         {
52                 conn = NULL;
53         }
54
55         void init()
56         {
57                 Implementation eventlist[] = { I_OnCheckReady, I_OnRehash, I_OnUserRegister };
58                 ServerInstance->Modules->Attach(eventlist, this, 3);
59                 OnRehash(NULL);
60         }
61
62         ~ModuleLDAPAuth()
63         {
64                 if (conn)
65                         ldap_unbind_ext(conn, NULL, NULL);
66         }
67
68         void OnRehash(User* user)
69         {
70                 ConfigReader Conf;
71
72                 base                    = Conf.ReadValue("ldapauth", "baserdn", 0);
73                 attribute               = Conf.ReadValue("ldapauth", "attribute", 0);
74                 ldapserver              = Conf.ReadValue("ldapauth", "server", 0);
75                 allowpattern            = Conf.ReadValue("ldapauth", "allowpattern", 0);
76                 killreason              = Conf.ReadValue("ldapauth", "killreason", 0);
77                 std::string scope       = Conf.ReadValue("ldapauth", "searchscope", 0);
78                 username                = Conf.ReadValue("ldapauth", "binddn", 0);
79                 password                = Conf.ReadValue("ldapauth", "bindauth", 0);
80                 verbose                 = Conf.ReadFlag("ldapauth", "verbose", 0);              /* Set to true if failed connects should be reported to operators */
81                 useusername             = Conf.ReadFlag("ldapauth", "userfield", 0);
82
83                 if (scope == "base")
84                         searchscope = LDAP_SCOPE_BASE;
85                 else if (scope == "onelevel")
86                         searchscope = LDAP_SCOPE_ONELEVEL;
87                 else searchscope = LDAP_SCOPE_SUBTREE;
88
89                 Connect();
90         }
91
92         bool Connect()
93         {
94                 if (conn != NULL)
95                         ldap_unbind_ext(conn, NULL, NULL);
96                 int res, v = LDAP_VERSION3;
97                 res = ldap_initialize(&conn, ldapserver.c_str());
98                 if (res != LDAP_SUCCESS)
99                 {
100                         if (verbose)
101                                 ServerInstance->SNO->WriteToSnoMask('c', "LDAP connection failed: %s", ldap_err2string(res));
102                         conn = NULL;
103                         return false;
104                 }
105
106                 res = ldap_set_option(conn, LDAP_OPT_PROTOCOL_VERSION, (void *)&v);
107                 if (res != LDAP_SUCCESS)
108                 {
109                         if (verbose)
110                                 ServerInstance->SNO->WriteToSnoMask('c', "LDAP set protocol to v3 failed: %s", ldap_err2string(res));
111                         ldap_unbind_ext(conn, NULL, NULL);
112                         conn = NULL;
113                         return false;
114                 }
115                 return true;
116         }
117
118         ModResult OnUserRegister(LocalUser* user)
119         {
120                 if ((!allowpattern.empty()) && (InspIRCd::Match(user->nick,allowpattern)))
121                 {
122                         ldapAuthed.set(user,1);
123                         return MOD_RES_PASSTHRU;
124                 }
125
126                 if (!CheckCredentials(user))
127                 {
128                         ServerInstance->Users->QuitUser(user, killreason);
129                         return MOD_RES_DENY;
130                 }
131                 return MOD_RES_PASSTHRU;
132         }
133
134         bool CheckCredentials(LocalUser* user)
135         {
136                 if (conn == NULL)
137                         if (!Connect())
138                                 return false;
139
140                 int res;
141                 // bind anonymously if no bind DN and authentication are given in the config
142                 struct berval cred;
143                 cred.bv_val = const_cast<char*>(password.c_str());
144                 cred.bv_len = password.length();
145
146                 if ((res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) != LDAP_SUCCESS)
147                 {
148                         if (res == LDAP_SERVER_DOWN)
149                         {
150                                 // Attempt to reconnect if the connection dropped
151                                 if (verbose)
152                                         ServerInstance->SNO->WriteToSnoMask('a', "LDAP server has gone away - reconnecting...");
153                                 Connect();
154                                 res = ldap_sasl_bind_s(conn, username.c_str(), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
155                         }
156
157                         if (res != LDAP_SUCCESS)
158                         {
159                                 if (verbose)
160                                         ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s!%s@%s (LDAP bind failed: %s)", user->nick.c_str(), user->ident.c_str(), user->host.c_str(), ldap_err2string(res));
161                                 ldap_unbind_ext(conn, NULL, NULL);
162                                 conn = NULL;
163                                 return false;
164                         }
165                 }
166
167                 LDAPMessage *msg, *entry;
168                 std::string what = (attribute + "=" + (useusername ? user->ident : user->nick));
169                 if ((res = ldap_search_ext_s(conn, base.c_str(), searchscope, what.c_str(), NULL, 0, NULL, NULL, NULL, 0, &msg)) != LDAP_SUCCESS)
170                 {
171                         if (verbose)
172                                 ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s!%s@%s (LDAP search failed: %s)", user->nick.c_str(), user->ident.c_str(), user->host.c_str(), ldap_err2string(res));
173                         return false;
174                 }
175                 if (ldap_count_entries(conn, msg) > 1)
176                 {
177                         if (verbose)
178                                 ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s!%s@%s (LDAP search returned more than one result: %s)", user->nick.c_str(), user->ident.c_str(), user->host.c_str(), ldap_err2string(res));
179                         ldap_msgfree(msg);
180                         return false;
181                 }
182                 if ((entry = ldap_first_entry(conn, msg)) == NULL)
183                 {
184                         if (verbose)
185                                 ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s!%s@%s (LDAP search returned no results: %s)", user->nick.c_str(), user->ident.c_str(), user->host.c_str(), ldap_err2string(res));
186                         ldap_msgfree(msg);
187                         return false;
188                 }
189                 if (user->password.empty())
190                 {
191                         if (verbose)
192                                 ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s!%s@%s (No password provided)", user->nick.c_str(), user->ident.c_str(), user->host.c_str());
193                         return false;
194                 }
195                 cred.bv_val = (char*)user->password.data();
196                 cred.bv_len = user->password.length();
197                 if ((res = ldap_sasl_bind_s(conn, ldap_get_dn(conn, entry), LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL)) == LDAP_SUCCESS)
198                 {
199                         ldap_msgfree(msg);
200                         ldapAuthed.set(user,1);
201                         return true;
202                 }
203                 else
204                 {
205                         if (verbose)
206                                 ServerInstance->SNO->WriteToSnoMask('c', "Forbidden connection from %s!%s@%s (%s)", user->nick.c_str(), user->ident.c_str(), user->host.c_str(), ldap_err2string(res));
207                         ldap_msgfree(msg);
208                         return false;
209                 }
210         }
211
212         ModResult OnCheckReady(LocalUser* user)
213         {
214                 return ldapAuthed.get(user) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
215         }
216
217         Version GetVersion()
218         {
219                 return Version("Allow/Deny connections based upon answer from LDAP server", VF_VENDOR);
220         }
221
222 };
223
224 MODULE_INIT(ModuleLDAPAuth)