Anope IRC Services  Version 2.0
ns_cert.cpp
Go to the documentation of this file.
1 /* NickServ core functions
2  *
3  * (C) 2003-2014 Anope Team
4  * Contact us at team@anope.org
5  *
6  * Please read COPYING and README for further details.
7  *
8  * Based on the original code of Epona by Lara.
9  * Based on the original code of Services by Andy Church.
10  */
11 
12 #include "module.h"
13 #include "modules/ns_cert.h"
14 
16 
18 {
20 
22  {
23  Anope::hash_map<NickCore *>::iterator it = certmap.find(cert);
24  if (it != certmap.end())
25  return it->second;
26  return NULL;
27  }
28 };
29 
31 {
33  std::vector<Anope::string> certs;
34 
35  public:
37 
39  {
40  ClearCert();
41  }
42 
50  {
51  this->certs.push_back(entry);
52  certmap[entry] = nc;
53  FOREACH_MOD(OnNickAddCert, (this->nc, entry));
54  }
55 
63  Anope::string GetCert(unsigned entry) const anope_override
64  {
65  if (entry >= this->certs.size())
66  return "";
67  return this->certs[entry];
68  }
69 
70  unsigned GetCertCount() const anope_override
71  {
72  return this->certs.size();
73  }
74 
82  bool FindCert(const Anope::string &entry) const anope_override
83  {
84  return std::find(this->certs.begin(), this->certs.end(), entry) != this->certs.end();
85  }
86 
94  {
95  std::vector<Anope::string>::iterator it = std::find(this->certs.begin(), this->certs.end(), entry);
96  if (it != this->certs.end())
97  {
98  FOREACH_MOD(OnNickEraseCert, (this->nc, entry));
99  certmap.erase(entry);
100  this->certs.erase(it);
101  }
102  }
103 
109  {
110  FOREACH_MOD(OnNickClearCert, (this->nc));
111  for (unsigned i = 0; i < certs.size(); ++i)
112  certmap.erase(certs[i]);
113  this->certs.clear();
114  }
115 
117  {
118  if (this->certs.empty())
119  nc->Shrink<NSCertList>("certificates");
120  }
121 
122  struct ExtensibleItem : ::ExtensibleItem<NSCertListImpl>
123  {
124  ExtensibleItem(Module *m, const Anope::string &ename) : ::ExtensibleItem<NSCertListImpl>(m, ename) { }
125 
127  {
128  if (s->GetSerializableType()->GetName() != "NickCore")
129  return;
130 
131  const NickCore *n = anope_dynamic_static_cast<const NickCore *>(e);
132  NSCertList *c = this->Get(n);
133  if (c == NULL || !c->GetCertCount())
134  return;
135 
136  for (unsigned i = 0; i < c->GetCertCount(); ++i)
137  data["cert"] << c->GetCert(i) << " ";
138  }
139 
141  {
142  if (s->GetSerializableType()->GetName() != "NickCore")
143  return;
144 
146  NSCertListImpl *c = this->Require(n);
147 
148  Anope::string buf;
149  data["cert"] >> buf;
150  spacesepstream sep(buf);
151  for (unsigned i = 0; i < c->certs.size(); ++i)
152  certmap.erase(c->certs[i]);
153  c->certs.clear();
154  while (sep.GetToken(buf))
155  {
156  c->certs.push_back(buf);
157  certmap[buf] = n;
158  }
159  }
160  };
161 };
162 
163 class CommandNSCert : public Command
164 {
165  private:
166  void DoAdd(CommandSource &source, NickCore *nc, Anope::string certfp)
167  {
168  NSCertList *cl = nc->Require<NSCertList>("certificates");
169  unsigned max = Config->GetModule(this->owner)->Get<unsigned>("max", "5");
170 
171  if (cl->GetCertCount() >= max)
172  {
173  source.Reply(_("Sorry, the maximum of %d certificate entries has been reached."), max);
174  return;
175  }
176 
177  if (source.GetAccount() == nc)
178  {
179  User *u = source.GetUser();
180 
181  if (!u || u->fingerprint.empty())
182  {
183  source.Reply(_("You are not using a client certificate."));
184  return;
185  }
186 
187  certfp = u->fingerprint;
188  }
189 
190  if (cl->FindCert(certfp))
191  {
192  source.Reply(_("Fingerprint \002%s\002 already present on %s's certificate list."), certfp.c_str(), nc->display.c_str());
193  return;
194  }
195 
196  if (certmap.find(certfp) != certmap.end())
197  {
198  source.Reply(_("Fingerprint \002%s\002 is already in use."), certfp.c_str());
199  return;
200  }
201 
202  cl->AddCert(certfp);
203  Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to ADD certificate fingerprint " << certfp << " to " << nc->display;
204  source.Reply(_("\002%s\002 added to %s's certificate list."), certfp.c_str(), nc->display.c_str());
205  }
206 
207  void DoDel(CommandSource &source, NickCore *nc, Anope::string certfp)
208  {
209  NSCertList *cl = nc->Require<NSCertList>("certificates");
210 
211  if (certfp.empty())
212  {
213  User *u = source.GetUser();
214  if (u)
215  certfp = u->fingerprint;
216  }
217 
218  if (certfp.empty())
219  {
220  this->OnSyntaxError(source, "DEL");
221  return;
222  }
223 
224  if (!cl->FindCert(certfp))
225  {
226  source.Reply(_("\002%s\002 not found on %s's certificate list."), certfp.c_str(), nc->display.c_str());
227  return;
228  }
229 
230  cl->EraseCert(certfp);
231  cl->Check();
232  Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to DELETE certificate fingerprint " << certfp << " from " << nc->display;
233  source.Reply(_("\002%s\002 deleted from %s's certificate list."), certfp.c_str(), nc->display.c_str());
234  }
235 
236  void DoList(CommandSource &source, const NickCore *nc)
237  {
238  NSCertList *cl = nc->GetExt<NSCertList>("certificates");
239 
240  if (!cl || !cl->GetCertCount())
241  {
242  source.Reply(_("%s's certificate list is empty."), nc->display.c_str());
243  return;
244  }
245 
246  source.Reply(_("Certificate list for %s:"), nc->display.c_str());
247  for (unsigned i = 0; i < cl->GetCertCount(); ++i)
248  {
249  Anope::string fingerprint = cl->GetCert(i);
250  source.Reply(" %s", fingerprint.c_str());
251  }
252  }
253 
254  public:
255  CommandNSCert(Module *creator) : Command(creator, "nickserv/cert", 1, 3)
256  {
257  this->SetDesc(_("Modify the nickname client certificate list"));
258  this->SetSyntax(_("ADD [\037nickname\037] [\037fingerprint\037]"));
259  this->SetSyntax(_("DEL [\037nickname\037] \037fingerprint\037"));
260  this->SetSyntax(_("LIST [\037nickname\037]"));
261  }
262 
263  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
264  {
265  const Anope::string &cmd = params[0];
266  Anope::string nick, certfp;
267 
268  if (cmd.equals_ci("LIST"))
269  nick = params.size() > 1 ? params[1] : "";
270  else
271  {
272  nick = params.size() == 3 ? params[1] : "";
273  certfp = params.size() > 1 ? params[params.size() - 1] : "";
274  }
275 
276  NickCore *nc;
277  if (!nick.empty())
278  {
279  const NickAlias *na = NickAlias::Find(nick);
280  if (na == NULL)
281  {
282  source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
283  return;
284  }
285  else if (na->nc != source.GetAccount() && !source.HasPriv("nickserv/access"))
286  {
287  source.Reply(ACCESS_DENIED);
288  return;
289  }
290  else if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && source.GetAccount() != na->nc && na->nc->IsServicesOper() && !cmd.equals_ci("LIST"))
291  {
292  source.Reply(_("You may view but not modify the certificate list of other Services Operators."));
293  return;
294  }
295 
296  nc = na->nc;
297  }
298  else
299  nc = source.nc;
300 
301  if (cmd.equals_ci("LIST"))
302  return this->DoList(source, nc);
303  else if (nc->HasExt("NS_SUSPENDED"))
304  source.Reply(NICK_X_SUSPENDED, nc->display.c_str());
305  else if (Anope::ReadOnly)
306  source.Reply(READ_ONLY_MODE);
307  else if (cmd.equals_ci("ADD"))
308  return this->DoAdd(source, nc, certfp);
309  else if (cmd.equals_ci("DEL"))
310  return this->DoDel(source, nc, certfp);
311  else
312  this->OnSyntaxError(source, "");
313  }
314 
315  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
316  {
317  this->SendSyntax(source);
318  source.Reply(" ");
319  source.Reply(_("Modifies or displays the certificate list for your nick.\n"
320  "If you connect to IRC and provide a client certificate with a\n"
321  "matching fingerprint in the cert list, your nick will be\n"
322  "automatically identified to services. Services Operators\n"
323  "may provide a nick to modify other users' certificate lists.\n"
324  " \n"));
325  source.Reply(_("Examples:\n"
326  " \n"
327  " \002CERT ADD <fingerprint>\002\n"
328  " Adds this fingerprint to the certificate list and\n"
329  " automatically identifies you when you connect to IRC\n"
330  " using this certificate.\n"
331  " \n"
332  " \002CERT DEL <fingerprint>\002\n"
333  " Reverses the previous command.\n"
334  " \n"
335  " \002CERT LIST\002\n"
336  " Displays the current certificate list."));
337  return true;
338  }
339 };
340 
341 class NSCert : public Module
342 {
346 
347  public:
348  NSCert(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
349  commandnscert(this), certs(this, "certificates"), cs(this)
350  {
351  if (!IRCD || !IRCD->CanCertFP)
352  throw ModuleException("Your IRCd does not support ssl client certificates");
353  }
354 
356  {
357  BotInfo *NickServ = Config->GetClient("NickServ");
358  if (!NickServ || u->IsIdentified())
359  return;
360 
361  NickCore *nc = cs.FindAccountFromCert(u->fingerprint);
362  if (!nc || nc->HasExt("NS_SUSPENDED"))
363  return;
364 
365  NickAlias *na = NickAlias::Find(u->nick);
366  if (na && na->nc == nc)
367  u->Identify(na);
368  else
369  u->Login(nc);
370 
371  u->SendMessage(NickServ, _("SSL certificate fingerprint accepted, you are now identified to \002%s\002."), nc->display.c_str());
372  Log(NickServ) << u->GetMask() << " automatically identified for account " << nc->display << " via SSL certificate fingerprint";
373  }
374 
376  {
377  NSCertList *cl = certs.Get(na->nc);
378  if (!u->fingerprint.empty() && cl && cl->FindCert(u->fingerprint))
379  {
380  BotInfo *NickServ = Config->GetClient("NickServ");
381  u->Identify(na);
382  u->SendMessage(NickServ, _("SSL certificate fingerprint accepted, you are now identified."));
383  Log(NickServ) << u->GetMask() << " automatically identified for account " << na->nc->display << " via SSL certificate fingerprint";
384  return EVENT_ALLOW;
385  }
386 
387  return EVENT_CONTINUE;
388  }
389 };
390 
Serialize::Reference< NickCore > nc
Definition: account.h:47
Definition: bots.h:24
CoreExport bool ReadOnly
Definition: main.cpp:28
static NickAlias * Find(const Anope::string &nick)
Definition: nickalias.cpp:121
CertServiceImpl cs
Definition: ns_cert.cpp:345
NickCore * nc
Definition: logger.h:61
virtual void Check()=0
virtual void AddCert(const Anope::string &entry)=0
EventReturn OnNickValidate(User *u, NickAlias *na) anope_override
Definition: ns_cert.cpp:375
Anope::string fingerprint
Definition: users.h:73
void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const anope_override
Definition: ns_cert.cpp:126
#define ACCESS_DENIED
Definition: language.h:73
Definition: users.h:34
#define NICK_X_NOT_REGISTERED
Definition: language.h:79
void Check() anope_override
Definition: ns_cert.cpp:116
void EraseCert(const Anope::string &entry) anope_override
Definition: ns_cert.cpp:93
void DoDel(CommandSource &source, NickCore *nc, Anope::string certfp)
Definition: ns_cert.cpp:207
ExtensibleItem(Module *m, const Anope::string &ename)
Definition: ns_cert.cpp:124
#define READ_ONLY_MODE
Definition: language.h:71
unsigned GetCertCount() const anope_override
Definition: ns_cert.cpp:70
NSCertListImpl(Extensible *obj)
Definition: ns_cert.cpp:36
void SetDesc(const Anope::string &d)
Definition: command.cpp:130
bool equals_ci(const char *_str) const
Definition: anope.h:78
CommandNSCert commandnscert
Definition: ns_cert.cpp:343
T * Require(const Anope::string &name)
Definition: extensible.h:244
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: ns_cert.cpp:263
void OnFingerprint(User *u) anope_override
Definition: ns_cert.cpp:355
#define FOREACH_MOD(ename, args)
Definition: modules.h:62
bool FindCert(const Anope::string &entry) const anope_override
Definition: ns_cert.cpp:82
NSCertListImpl * Require(Extensible *obj)
Definition: extensible.h:116
Serialize::Reference< NickCore > nc
Definition: ns_cert.cpp:32
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: ns_cert.cpp:315
NickCore * FindAccountFromCert(const Anope::string &cert) anope_override
Definition: ns_cert.cpp:21
Definition: Config.cs:26
bool CanCertFP
Definition: protocol.h:67
void Shrink(const Anope::string &name)
Definition: extensible.h:253
NSCertListImpl::ExtensibleItem certs
Definition: ns_cert.cpp:344
virtual void OnSyntaxError(CommandSource &source, const Anope::string &subcommand)
Definition: command.cpp:191
void Reply(const char *message,...)
Definition: command.cpp:96
void DoAdd(CommandSource &source, NickCore *nc, Anope::string certfp)
Definition: ns_cert.cpp:166
std::vector< Anope::string > certs
Definition: ns_cert.cpp:33
bool IsIdentified(bool check_nick=false) const
Definition: users.cpp:427
Anope::string display
Definition: account.h:113
virtual bool FindCert(const Anope::string &entry) const =0
#define anope_override
Definition: services.h:56
bool empty() const
Definition: anope.h:126
CoreExport IRCDProto * IRCD
Definition: protocol.cpp:23
EventReturn
Definition: modules.h:129
#define MODULE_INIT(x)
Definition: modules.h:45
User * GetUser()
Definition: command.cpp:31
void SetSyntax(const Anope::string &s)
Definition: command.cpp:140
CommandNSCert(Module *creator)
Definition: ns_cert.cpp:255
void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override
Definition: ns_cert.cpp:140
virtual void EraseCert(const Anope::string &entry)=0
bool GetToken(Anope::string &token)
Definition: hashcomp.cpp:99
NSCertListImpl * Get(const Extensible *obj) const
Definition: extensible.h:103
T anope_dynamic_static_cast(O ptr)
Definition: anope.h:774
Anope::string GetCert(unsigned entry) const anope_override
Definition: ns_cert.cpp:63
void SendSyntax(CommandSource &)
Definition: command.cpp:145
NickCore * GetAccount()
Definition: command.cpp:36
virtual Anope::string GetCert(unsigned entry) const =0
virtual unsigned GetCertCount() const =0
const char * c_str() const
Definition: anope.h:117
Definition: logger.h:53
CertServiceImpl(Module *o)
Definition: ns_cert.cpp:19
static Anope::hash_map< NickCore * > certmap
Definition: ns_cert.cpp:15
T * GetExt(const Anope::string &name) const
Definition: extensible.h:213
void AddCert(const Anope::string &entry) anope_override
Definition: ns_cert.cpp:49
void DoList(CommandSource &source, const NickCore *nc)
Definition: ns_cert.cpp:236
#define _(x)
Definition: services.h:50
bool HasExt(const Anope::string &name) const
Definition: extensible.cpp:31
NSCert(const Anope::string &modname, const Anope::string &creator)
Definition: ns_cert.cpp:348
#define NICK_X_SUSPENDED
Definition: language.h:82
void ClearCert() anope_override
Definition: ns_cert.cpp:108
Module * owner
Definition: service.h:84