]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ssl_gnutls.cpp
Create StreamSocket for IO hooking implementation
[user/henk/code/inspircd.git] / src / modules / extra / m_ssl_gnutls.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 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 <gnutls/gnutls.h>
16 #include <gnutls/x509.h>
17 #include "transport.h"
18 #include "m_cap.h"
19
20 #ifdef WINDOWS
21 #pragma comment(lib, "libgnutls-13.lib")
22 #endif
23
24 /* $ModDesc: Provides SSL support for clients */
25 /* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") */
26 /* $LinkerFlags: rpath("pkg-config --libs gnutls") pkgconflibs("gnutls","/libgnutls.so","-lgnutls") */
27 /* $ModDep: transport.h */
28 /* $CopyInstall: conf/key.pem $(CONPATH) */
29 /* $CopyInstall: conf/cert.pem $(CONPATH) */
30
31 enum issl_status { ISSL_NONE, ISSL_HANDSHAKING_READ, ISSL_HANDSHAKING_WRITE, ISSL_HANDSHAKEN, ISSL_CLOSING, ISSL_CLOSED };
32
33 static gnutls_x509_crt_t x509_cert;
34 static gnutls_x509_privkey_t x509_key;
35 static int cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs,
36         const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st) {
37
38         st->type = GNUTLS_CRT_X509;
39         st->ncerts = 1;
40         st->cert.x509 = &x509_cert;
41         st->key.x509 = x509_key;
42         st->deinit_all = 0;
43
44         return 0;
45 }
46
47 /** Represents an SSL user's extra data
48  */
49 class issl_session : public classbase
50 {
51 public:
52         issl_session()
53         {
54                 sess = NULL;
55         }
56
57         gnutls_session_t sess;
58         issl_status status;
59 };
60
61 class CommandStartTLS : public Command
62 {
63  public:
64         CommandStartTLS (Module* mod) : Command(mod, "STARTTLS")
65         {
66                 works_before_reg = true;
67         }
68
69         CmdResult Handle (const std::vector<std::string> &parameters, User *user)
70         {
71                 /* changed from == REG_ALL to catch clients sending STARTTLS
72                  * after NICK and USER but before OnUserConnect completes and
73                  * give a proper error message (see bug #645) - dz
74                  */
75                 if (user->registered != REG_NONE)
76                 {
77                         user->WriteNumeric(691, "%s :STARTTLS is not permitted after client registration has started", user->nick.c_str());
78                 }
79                 else
80                 {
81                         if (!user->GetIOHook())
82                         {
83                                 user->WriteNumeric(670, "%s :STARTTLS successful, go ahead with TLS handshake", user->nick.c_str());
84                                 user->AddIOHook(creator);
85                                 creator->OnStreamSocketAccept(user, NULL, NULL);
86                         }
87                         else
88                                 user->WriteNumeric(691, "%s :STARTTLS failure", user->nick.c_str());
89                 }
90
91                 return CMD_FAILURE;
92         }
93 };
94
95 class ModuleSSLGnuTLS : public Module
96 {
97         std::set<ListenSocketBase*> listenports;
98
99         issl_session* sessions;
100
101         gnutls_certificate_credentials x509_cred;
102         gnutls_dh_params dh_params;
103
104         std::string keyfile;
105         std::string certfile;
106         std::string cafile;
107         std::string crlfile;
108         std::string sslports;
109         int dh_bits;
110
111         bool cred_alloc;
112
113         CommandStartTLS starttls;
114
115         GenericCap capHandler;
116  public:
117
118         ModuleSSLGnuTLS(InspIRCd* Me)
119                 : Module(Me), starttls(this), capHandler(this, "tls")
120         {
121                 ServerInstance->Modules->PublishInterface("BufferedSocketHook", this);
122
123                 sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
124
125                 gnutls_global_init(); // This must be called once in the program
126                 gnutls_x509_crt_init(&x509_cert);
127                 gnutls_x509_privkey_init(&x509_key);
128
129                 cred_alloc = false;
130                 // Needs the flag as it ignores a plain /rehash
131                 OnModuleRehash(NULL,"ssl");
132
133                 // Void return, guess we assume success
134                 gnutls_certificate_set_dh_params(x509_cred, dh_params);
135                 Implementation eventlist[] = { I_On005Numeric, I_OnRequest, I_OnRehash, I_OnModuleRehash, I_OnPostConnect,
136                         I_OnEvent, I_OnHookIO };
137                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
138
139                 ServerInstance->AddCommand(&starttls);
140         }
141
142         void OnRehash(User* user)
143         {
144                 ConfigReader Conf(ServerInstance);
145
146                 listenports.clear();
147                 sslports.clear();
148
149                 for (size_t i = 0; i < ServerInstance->ports.size(); i++)
150                 {
151                         ListenSocketBase* port = ServerInstance->ports[i];
152                         std::string desc = port->GetDescription();
153                         if (desc != "gnutls")
154                                 continue;
155
156                         listenports.insert(port);
157                         std::string portid = port->GetBindDesc();
158
159                         ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls.so: Enabling SSL for port %s", portid.c_str());
160                         if (port->GetIP() != "127.0.0.1")
161                                 sslports.append(portid).append(";");
162                 }
163
164                 if (!sslports.empty())
165                         sslports.erase(sslports.end() - 1);
166         }
167
168         void OnModuleRehash(User* user, const std::string &param)
169         {
170                 if(param != "ssl")
171                         return;
172
173                 OnRehash(user);
174
175                 ConfigReader Conf(ServerInstance);
176
177                 std::string confdir(ServerInstance->ConfigFileName);
178                 // +1 so we the path ends with a /
179                 confdir = confdir.substr(0, confdir.find_last_of('/') + 1);
180
181                 cafile = Conf.ReadValue("gnutls", "cafile", 0);
182                 crlfile = Conf.ReadValue("gnutls", "crlfile", 0);
183                 certfile = Conf.ReadValue("gnutls", "certfile", 0);
184                 keyfile = Conf.ReadValue("gnutls", "keyfile", 0);
185                 dh_bits = Conf.ReadInteger("gnutls", "dhbits", 0, false);
186
187                 // Set all the default values needed.
188                 if (cafile.empty())
189                         cafile = "ca.pem";
190
191                 if (crlfile.empty())
192                         crlfile = "crl.pem";
193
194                 if (certfile.empty())
195                         certfile = "cert.pem";
196
197                 if (keyfile.empty())
198                         keyfile = "key.pem";
199
200                 if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096))
201                         dh_bits = 1024;
202
203                 // Prepend relative paths with the path to the config directory.
204                 if ((cafile[0] != '/') && (!ServerInstance->Config->StartsWithWindowsDriveLetter(cafile)))
205                         cafile = confdir + cafile;
206
207                 if ((crlfile[0] != '/') && (!ServerInstance->Config->StartsWithWindowsDriveLetter(crlfile)))
208                         crlfile = confdir + crlfile;
209
210                 if ((certfile[0] != '/') && (!ServerInstance->Config->StartsWithWindowsDriveLetter(certfile)))
211                         certfile = confdir + certfile;
212
213                 if ((keyfile[0] != '/') && (!ServerInstance->Config->StartsWithWindowsDriveLetter(keyfile)))
214                         keyfile = confdir + keyfile;
215
216                 int ret;
217
218                 if (cred_alloc)
219                 {
220                         // Deallocate the old credentials
221                         gnutls_dh_params_deinit(dh_params);
222                         gnutls_certificate_free_credentials(x509_cred);
223                 }
224                 else
225                         cred_alloc = true;
226
227                 if((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0)
228                         ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to allocate certificate credentials: %s", gnutls_strerror(ret));
229
230                 if((ret =gnutls_certificate_set_x509_trust_file(x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
231                         ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret));
232
233                 if((ret = gnutls_certificate_set_x509_crl_file (x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
234                         ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret));
235
236                 FileReader reader(ServerInstance);
237
238                 reader.LoadFile(certfile);
239                 std::string cert_string = reader.Contents();
240                 gnutls_datum_t cert_datum = { (unsigned char*)cert_string.data(), cert_string.length() };
241
242                 reader.LoadFile(keyfile);
243                 std::string key_string = reader.Contents();
244                 gnutls_datum_t key_datum = { (unsigned char*)key_string.data(), key_string.length() };
245
246                 // If this fails, no SSL port will work. At all. So, do the smart thing - throw a ModuleException
247                 if((ret = gnutls_x509_crt_import(x509_cert, &cert_datum, GNUTLS_X509_FMT_PEM)) < 0)
248                         throw ModuleException("Unable to load GnuTLS server certificate (" + certfile + "): " + std::string(gnutls_strerror(ret)));
249
250                 if((ret = gnutls_x509_privkey_import(x509_key, &key_datum, GNUTLS_X509_FMT_PEM)) < 0)
251                         throw ModuleException("Unable to load GnuTLS server private key (" + keyfile + "): " + std::string(gnutls_strerror(ret)));
252
253                 if((ret = gnutls_certificate_set_x509_key(x509_cred, &x509_cert, 1, x509_key)) < 0)
254                         throw ModuleException("Unable to set GnuTLS cert/key pair: " + std::string(gnutls_strerror(ret)));
255
256                 gnutls_certificate_client_set_retrieve_function (x509_cred, cert_callback);
257
258                 if((ret = gnutls_dh_params_init(&dh_params)) < 0)
259                         ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters: %s", gnutls_strerror(ret));
260
261                 // This may be on a large (once a day or week) timer eventually.
262                 GenerateDHParams();
263         }
264
265         void GenerateDHParams()
266         {
267                 // Generate Diffie Hellman parameters - for use with DHE
268                 // kx algorithms. These should be discarded and regenerated
269                 // once a day, once a week or once a month. Depending on the
270                 // security requirements.
271
272                 int ret;
273
274                 if((ret = gnutls_dh_params_generate2(dh_params, dh_bits)) < 0)
275                         ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to generate DH parameters (%d bits): %s", dh_bits, gnutls_strerror(ret));
276         }
277
278         ~ModuleSSLGnuTLS()
279         {
280                 gnutls_x509_crt_deinit(x509_cert);
281                 gnutls_x509_privkey_deinit(x509_key);
282                 gnutls_dh_params_deinit(dh_params);
283                 gnutls_certificate_free_credentials(x509_cred);
284                 gnutls_global_deinit();
285                 ServerInstance->Modules->UnpublishInterface("BufferedSocketHook", this);
286                 delete[] sessions;
287         }
288
289         void OnCleanup(int target_type, void* item)
290         {
291                 if(target_type == TYPE_USER)
292                 {
293                         User* user = static_cast<User*>(item);
294
295                         if (user->GetIOHook() == this)
296                         {
297                                 // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
298                                 // Potentially there could be multiple SSL modules loaded at once on different ports.
299                                 ServerInstance->Users->QuitUser(user, "SSL module unloading");
300                                 user->DelIOHook();
301                         }
302                 }
303         }
304
305         Version GetVersion()
306         {
307                 return Version("$Id$", VF_VENDOR, API_VERSION);
308         }
309
310
311         void On005Numeric(std::string &output)
312         {
313                 if (!sslports.empty())
314                         output.append(" SSL=" + sslports);
315                 output.append(" STARTTLS");
316         }
317
318         void OnHookIO(StreamSocket* user, ListenSocketBase* lsb)
319         {
320                 if (!user->GetIOHook() && listenports.find(lsb) != listenports.end())
321                 {
322                         /* Hook the user with our module */
323                         user->AddIOHook(this);
324                 }
325         }
326
327         const char* OnRequest(Request* request)
328         {
329                 ISHRequest* ISR = static_cast<ISHRequest*>(request);
330                 if (strcmp("IS_NAME", request->GetId()) == 0)
331                 {
332                         return "gnutls";
333                 }
334                 else if (strcmp("IS_HOOK", request->GetId()) == 0)
335                 {
336                         ISR->Sock->AddIOHook(this);
337                         return "OK";
338                 }
339                 else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
340                 {
341                         ISR->Sock->DelIOHook();
342                         return "OK";
343                 }
344                 else if (strcmp("IS_HSDONE", request->GetId()) == 0)
345                 {
346                         if (ISR->Sock->GetFd() < 0)
347                                 return "OK";
348
349                         issl_session* session = &sessions[ISR->Sock->GetFd()];
350                         return (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE) ? NULL : "OK";
351                 }
352                 else if (strcmp("IS_ATTACH", request->GetId()) == 0)
353                 {
354                         if (ISR->Sock->GetFd() > -1)
355                         {
356                                 issl_session* session = &sessions[ISR->Sock->GetFd()];
357                                 if (session->sess)
358                                 {
359                                         if (static_cast<Extensible*>(ServerInstance->SE->GetRef(ISR->Sock->GetFd())) == static_cast<Extensible*>(ISR->Sock))
360                                         {
361                                                 return "OK";
362                                         }
363                                 }
364                         }
365                 }
366                 else if (strcmp("GET_CERT", request->GetId()) == 0)
367                 {
368                         Module* sslinfo = ServerInstance->Modules->Find("m_sslinfo.so");
369                         if (sslinfo)
370                                 return sslinfo->OnRequest(request);
371                 }
372                 return NULL;
373         }
374
375
376         void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
377         {
378                 int fd = user->GetFd();
379                 issl_session* session = &sessions[fd];
380
381                 /* For STARTTLS: Don't try and init a session on a socket that already has a session */
382                 if (session->sess)
383                         return;
384
385                 gnutls_init(&session->sess, GNUTLS_SERVER);
386
387                 gnutls_set_default_priority(session->sess); // Avoid calling all the priority functions, defaults are adequate.
388                 gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred);
389                 gnutls_dh_set_prime_bits(session->sess, dh_bits);
390
391                 gnutls_transport_set_ptr(session->sess, reinterpret_cast<gnutls_transport_ptr_t>(fd)); // Give gnutls the fd for the socket.
392
393                 gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
394
395                 Handshake(session, user);
396         }
397
398         void OnStreamSocketConnect(StreamSocket* user)
399         {
400                 issl_session* session = &sessions[user->GetFd()];
401
402                 gnutls_init(&session->sess, GNUTLS_CLIENT);
403
404                 gnutls_set_default_priority(session->sess); // Avoid calling all the priority functions, defaults are adequate.
405                 gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred);
406                 gnutls_dh_set_prime_bits(session->sess, dh_bits);
407                 gnutls_transport_set_ptr(session->sess, reinterpret_cast<gnutls_transport_ptr_t>(user->GetFd()));
408
409                 Handshake(session, user);
410         }
411
412         void OnStreamSocketClose(StreamSocket* user)
413         {
414                 CloseSession(&sessions[user->GetFd()]);
415         }
416
417         int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
418         {
419                 issl_session* session = &sessions[user->GetFd()];
420
421                 if (!session->sess)
422                 {
423                         CloseSession(session);
424                         user->SetError("No SSL session");
425                         return -1;
426                 }
427
428                 if (session->status == ISSL_HANDSHAKING_READ)
429                 {
430                         // The handshake isn't finished, try to finish it.
431
432                         if(!Handshake(session, user))
433                         {
434                                 if (session->status != ISSL_CLOSING)
435                                         return 0;
436                                 user->SetError("Handshake Failed");
437                                 return -1;
438                         }
439                 }
440                 else if (session->status == ISSL_HANDSHAKING_WRITE)
441                 {
442                         MakePollWrite(user);
443                         return 0;
444                 }
445
446                 // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN.
447
448                 if (session->status == ISSL_HANDSHAKEN)
449                 {
450                         char* buffer = ServerInstance->GetReadBuffer();
451                         size_t bufsiz = ServerInstance->Config->NetBufferSize;
452                         size_t len = 0;
453                         while (len < bufsiz)
454                         {
455                                 int ret = gnutls_record_recv(session->sess, buffer + len, bufsiz - len);
456                                 if (ret > 0)
457                                 {
458                                         len += ret;
459                                 }
460                                 else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
461                                 {
462                                         break;
463                                 }
464                                 else if (ret == 0)
465                                 {
466                                         user->SetError("SSL Connection closed");
467                                         CloseSession(session);
468                                         return -1;
469                                 }
470                                 else
471                                 {
472                                         user->SetError(gnutls_strerror(ret));
473                                         CloseSession(session);
474                                         return -1;
475                                 }
476                         }
477                         if (len)
478                         {
479                                 recvq.append(buffer, len);
480                                 return 1;
481                         }
482                 }
483                 else if (session->status == ISSL_CLOSING)
484                         return -1;
485
486                 return 0;
487         }
488
489         int OnStreamSocketWrite(StreamSocket* user, std::string& sendq)
490         {
491                 issl_session* session = &sessions[user->GetFd()];
492
493                 if (!session->sess)
494                 {
495                         CloseSession(session);
496                         user->SetError("No SSL session");
497                         return -1;
498                 }
499
500                 if (session->status == ISSL_HANDSHAKING_WRITE || session->status == ISSL_HANDSHAKING_READ)
501                 {
502                         // The handshake isn't finished, try to finish it.
503                         Handshake(session, user);
504                         if (session->status != ISSL_CLOSING)
505                                 return 0;
506                         user->SetError("Handshake Failed");
507                         return -1;
508                 }
509
510                 int ret = 0;
511
512                 if (session->status == ISSL_HANDSHAKEN)
513                 {
514                         ret = gnutls_record_send(session->sess, sendq.data(), sendq.length());
515
516                         if (ret == (int)sendq.length())
517                         {
518                                 return 1;
519                         }
520                         else if (ret > 0)
521                         {
522                                 sendq = sendq.substr(ret);
523                                 MakePollWrite(user);
524                                 return 0;
525                         }
526                         else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
527                         {
528                                 MakePollWrite(user);
529                                 return 0;
530                         }
531                         else if (ret == 0)
532                         {
533                                 CloseSession(session);
534                                 user->SetError("SSL Connection closed");
535                                 return -1;
536                         }
537                         else // (ret < 0)
538                         {
539                                 user->SetError(gnutls_strerror(ret));
540                                 CloseSession(session);
541                                 return -1;
542                         }
543                 }
544
545                 return 0;
546         }
547
548         bool Handshake(issl_session* session, EventHandler* user)
549         {
550                 int ret = gnutls_handshake(session->sess);
551
552                 if (ret < 0)
553                 {
554                         if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
555                         {
556                                 // Handshake needs resuming later, read() or write() would have blocked.
557
558                                 if(gnutls_record_get_direction(session->sess) == 0)
559                                 {
560                                         // gnutls_handshake() wants to read() again.
561                                         session->status = ISSL_HANDSHAKING_READ;
562                                 }
563                                 else
564                                 {
565                                         // gnutls_handshake() wants to write() again.
566                                         session->status = ISSL_HANDSHAKING_WRITE;
567                                         MakePollWrite(user);
568                                 }
569                         }
570                         else
571                         {
572                                 CloseSession(session);
573                                 session->status = ISSL_CLOSING;
574                         }
575
576                         return false;
577                 }
578                 else
579                 {
580                         // Change the seesion state
581                         session->status = ISSL_HANDSHAKEN;
582
583                         VerifyCertificate(session,user);
584
585                         // Finish writing, if any left
586                         MakePollWrite(user);
587
588                         return true;
589                 }
590         }
591
592         void OnPostConnect(User* user)
593         {
594                 // This occurs AFTER OnUserConnect so we can be sure the
595                 // protocol module has propagated the NICK message.
596                 if (user->GetIOHook() == this && (IS_LOCAL(user)))
597                 {
598                         if (sessions[user->GetFd()].sess)
599                         {
600                                 std::string cipher = gnutls_kx_get_name(gnutls_kx_get(sessions[user->GetFd()].sess));
601                                 cipher.append("-").append(gnutls_cipher_get_name(gnutls_cipher_get(sessions[user->GetFd()].sess))).append("-");
602                                 cipher.append(gnutls_mac_get_name(gnutls_mac_get(sessions[user->GetFd()].sess)));
603                                 user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), cipher.c_str());
604                         }
605                 }
606         }
607
608         void MakePollWrite(EventHandler* eh)
609         {
610                 ServerInstance->SE->WantWrite(eh);
611         }
612
613         void CloseSession(issl_session* session)
614         {
615                 if(session->sess)
616                 {
617                         gnutls_bye(session->sess, GNUTLS_SHUT_WR);
618                         gnutls_deinit(session->sess);
619                 }
620
621                 session->sess = NULL;
622                 session->status = ISSL_NONE;
623         }
624
625         void VerifyCertificate(issl_session* session, Extensible* user)
626         {
627                 if (!session->sess || !user)
628                         return;
629
630                 Module* sslinfo = ServerInstance->Modules->Find("m_sslinfo.so");
631                 if (!sslinfo)
632                         return;
633
634                 unsigned int status;
635                 const gnutls_datum_t* cert_list;
636                 int ret;
637                 unsigned int cert_list_size;
638                 gnutls_x509_crt_t cert;
639                 char name[MAXBUF];
640                 unsigned char digest[MAXBUF];
641                 size_t digest_size = sizeof(digest);
642                 size_t name_size = sizeof(name);
643                 ssl_cert* certinfo = new ssl_cert;
644
645                 /* This verification function uses the trusted CAs in the credentials
646                  * structure. So you must have installed one or more CA certificates.
647                  */
648                 ret = gnutls_certificate_verify_peers2(session->sess, &status);
649
650                 if (ret < 0)
651                 {
652                         certinfo->error = std::string(gnutls_strerror(ret));
653                         goto info_done;
654                 }
655
656                 certinfo->invalid = (status & GNUTLS_CERT_INVALID);
657                 certinfo->unknownsigner = (status & GNUTLS_CERT_SIGNER_NOT_FOUND);
658                 certinfo->revoked = (status & GNUTLS_CERT_REVOKED);
659                 certinfo->trusted = !(status & GNUTLS_CERT_SIGNER_NOT_CA);
660
661                 /* Up to here the process is the same for X.509 certificates and
662                  * OpenPGP keys. From now on X.509 certificates are assumed. This can
663                  * be easily extended to work with openpgp keys as well.
664                  */
665                 if (gnutls_certificate_type_get(session->sess) != GNUTLS_CRT_X509)
666                 {
667                         certinfo->error = "No X509 keys sent";
668                         goto info_done;
669                 }
670
671                 ret = gnutls_x509_crt_init(&cert);
672                 if (ret < 0)
673                 {
674                         certinfo->error = gnutls_strerror(ret);
675                         goto info_done;
676                 }
677
678                 cert_list_size = 0;
679                 cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size);
680                 if (cert_list == NULL)
681                 {
682                         certinfo->error = "No certificate was found";
683                         goto info_done_dealloc;
684                 }
685
686                 /* This is not a real world example, since we only check the first
687                  * certificate in the given chain.
688                  */
689
690                 ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
691                 if (ret < 0)
692                 {
693                         certinfo->error = gnutls_strerror(ret);
694                         goto info_done_dealloc;
695                 }
696
697                 gnutls_x509_crt_get_dn(cert, name, &name_size);
698                 certinfo->dn = name;
699
700                 gnutls_x509_crt_get_issuer_dn(cert, name, &name_size);
701                 certinfo->issuer = name;
702
703                 if ((ret = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_MD5, digest, &digest_size)) < 0)
704                 {
705                         certinfo->error = gnutls_strerror(ret);
706                 }
707                 else
708                 {
709                         certinfo->fingerprint = irc::hex(digest, digest_size);
710                 }
711
712                 /* Beware here we do not check for errors.
713                  */
714                 if ((gnutls_x509_crt_get_expiration_time(cert) < ServerInstance->Time()) || (gnutls_x509_crt_get_activation_time(cert) > ServerInstance->Time()))
715                 {
716                         certinfo->error = "Not activated, or expired certificate";
717                 }
718
719 info_done_dealloc:
720                 gnutls_x509_crt_deinit(cert);
721 info_done:
722                 BufferedSocketFingerprintSubmission(user, this, sslinfo, certinfo).Send();
723         }
724
725         void OnEvent(Event* ev)
726         {
727                 capHandler.HandleEvent(ev);
728         }
729 };
730
731 MODULE_INIT(ModuleSSLGnuTLS)