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