]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_sqlauth.cpp
Add a short message at the top of permchannel DB, and ensure config format is compat
[user/henk/code/inspircd.git] / src / modules / m_sqlauth.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
14 #include "inspircd.h"
15 #include "m_sqlv2.h"
16 #include "m_sqlutils.h"
17 #include "hash.h"
18
19 /* $ModDesc: Allow/Deny connections based upon an arbitary SQL table */
20
21 class ModuleSQLAuth : public Module
22 {
23         LocalIntExt sqlAuthed;
24         Module* SQLutils;
25         Module* SQLprovider;
26
27         std::string freeformquery;
28         std::string killreason;
29         std::string allowpattern;
30         std::string databaseid;
31
32         bool verbose;
33
34 public:
35         ModuleSQLAuth() : sqlAuthed("sqlauth", this)
36         {
37                 SQLutils = ServerInstance->Modules->Find("m_sqlutils.so");
38                 if (!SQLutils)
39                         throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqlauth.so.");
40
41                 ServiceProvider* prov = ServerInstance->Modules->FindService(SERVICE_DATA, "SQL");
42                 if (!prov)
43                         throw ModuleException("Can't find an SQL provider module. Please load one before attempting to load m_sqlauth.");
44                 SQLprovider = prov->creator;
45
46                 OnRehash(NULL);
47                 Implementation eventlist[] = { I_OnUserDisconnect, I_OnCheckReady, I_OnRehash, I_OnUserRegister };
48                 ServerInstance->Modules->Attach(eventlist, this, 4);
49         }
50
51         virtual ~ModuleSQLAuth()
52         {
53         }
54
55         void OnRehash(User* user)
56         {
57                 ConfigReader Conf;
58
59                 databaseid      = Conf.ReadValue("sqlauth", "dbid", 0);                 /* Database ID, given to the SQL service provider */
60                 freeformquery   = Conf.ReadValue("sqlauth", "query", 0);        /* Field name where username can be found */
61                 killreason      = Conf.ReadValue("sqlauth", "killreason", 0);   /* Reason to give when access is denied to a user (put your reg details here) */
62                 allowpattern    = Conf.ReadValue("sqlauth", "allowpattern",0 ); /* Allow nicks matching this pattern without requiring auth */
63                 verbose         = Conf.ReadFlag("sqlauth", "verbose", 0);               /* Set to true if failed connects should be reported to operators */
64         }
65
66         ModResult OnUserRegister(LocalUser* user)
67         {
68                 if ((!allowpattern.empty()) && (InspIRCd::Match(user->nick,allowpattern)))
69                 {
70                         sqlAuthed.set(user, 1);
71                         return MOD_RES_PASSTHRU;
72                 }
73
74                 if (!CheckCredentials(user))
75                 {
76                         ServerInstance->Users->QuitUser(user, killreason);
77                         return MOD_RES_DENY;
78                 }
79                 return MOD_RES_PASSTHRU;
80         }
81
82         bool CheckCredentials(LocalUser* user)
83         {
84                 std::string thisquery = freeformquery;
85                 std::string safepass = user->password;
86                 std::string safegecos = user->fullname;
87
88                 /* Search and replace the escaped nick and escaped pass into the query */
89
90                 SearchAndReplace(safepass, std::string("\""), std::string("\\\""));
91                 SearchAndReplace(safegecos, std::string("\""), std::string("\\\""));
92
93                 SearchAndReplace(thisquery, std::string("$nick"), user->nick);
94                 SearchAndReplace(thisquery, std::string("$pass"), safepass);
95                 SearchAndReplace(thisquery, std::string("$host"), user->host);
96                 SearchAndReplace(thisquery, std::string("$ip"), std::string(user->GetIPString()));
97                 SearchAndReplace(thisquery, std::string("$gecos"), safegecos);
98                 SearchAndReplace(thisquery, std::string("$ident"), user->ident);
99                 SearchAndReplace(thisquery, std::string("$server"), std::string(user->server));
100                 SearchAndReplace(thisquery, std::string("$uuid"), user->uuid);
101
102                 HashProvider* md5 = ServerInstance->Modules->FindDataService<HashProvider>("hash/md5");
103                 if (md5)
104                         SearchAndReplace(thisquery, std::string("$md5pass"), md5->hexsum(user->password));
105
106                 HashProvider* sha256 = ServerInstance->Modules->FindDataService<HashProvider>("hash/sha256");
107                 if (sha256)
108                         SearchAndReplace(thisquery, std::string("$sha256pass"), sha256->hexsum(user->password));
109
110                 /* Build the query */
111                 SQLrequest req = SQLrequest(this, SQLprovider, databaseid, SQLquery(thisquery));
112
113                 req.Send();
114                 /* When we get the query response from the service provider we will be given an ID to play with,
115                  * just an ID number which is unique to this query. We need a way of associating that ID with a User
116                  * so we insert it into a map mapping the IDs to users.
117                  * Thankfully m_sqlutils provides this, it will associate a ID with a user or channel, and if the user quits it removes the
118                  * association. This means that if the user quits during a query we will just get a failed lookup from m_sqlutils - telling
119                  * us to discard the query.
120                  */
121                 AssociateUser(this, SQLutils, req.id, user).Send();
122
123                 return true;
124         }
125
126         void OnRequest(Request& request)
127         {
128                 if(strcmp(SQLRESID, request.id) == 0)
129                 {
130                         SQLresult* res = static_cast<SQLresult*>(&request);
131
132                         User* user = GetAssocUser(this, SQLutils, res->id).S().user;
133                         UnAssociate(this, SQLutils, res->id).S();
134
135                         if(user)
136                         {
137                                 if(res->error.Id() == SQL_NO_ERROR)
138                                 {
139                                         if(res->Rows())
140                                         {
141                                                 /* We got a row in the result, this is enough really */
142                                                 sqlAuthed.set(user, 1);
143                                         }
144                                         else if (verbose)
145                                         {
146                                                 /* No rows in result, this means there was no record matching the user */
147                                                 ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s!%s@%s (SQL query returned no matches)", user->nick.c_str(), user->ident.c_str(), user->host.c_str());
148                                         }
149                                 }
150                                 else if (verbose)
151                                 {
152                                         ServerInstance->SNO->WriteGlobalSno('a', "Forbidden connection from %s!%s@%s (SQL query failed: %s)", user->nick.c_str(), user->ident.c_str(), user->host.c_str(), res->error.Str());
153                                 }
154                         }
155                         else
156                         {
157                                 return;
158                         }
159
160                         if (!sqlAuthed.get(user))
161                         {
162                                 ServerInstance->Users->QuitUser(user, killreason);
163                         }
164                 }
165         }
166
167         ModResult OnCheckReady(LocalUser* user)
168         {
169                 return sqlAuthed.get(user) ? MOD_RES_PASSTHRU : MOD_RES_DENY;
170         }
171
172         Version GetVersion()
173         {
174                 return Version("Allow/Deny connections based upon an arbitary SQL table", VF_VENDOR);
175         }
176
177 };
178
179 MODULE_INIT(ModuleSQLAuth)