]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ssl_openssl.cpp
Extbans can be VF_OPTCOMMON as they do not desync on module add/remove
[user/henk/code/inspircd.git] / src / modules / extra / m_ssl_openssl.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 <openssl/ssl.h>
16 #include <openssl/err.h>
17 #include "ssl.h"
18
19 #ifdef WINDOWS
20 #pragma comment(lib, "libeay32MTd")
21 #pragma comment(lib, "ssleay32MTd")
22 #undef MAX_DESCRIPTORS
23 #define MAX_DESCRIPTORS 10000
24 #endif
25
26 /* $ModDesc: Provides SSL support for clients */
27
28 /* $LinkerFlags: if("USE_FREEBSD_BASE_SSL") -lssl -lcrypto */
29 /* $CompileFlags: if(!"USE_FREEBSD_BASE_SSL") pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */
30 /* $LinkerFlags: if(!"USE_FREEBSD_BASE_SSL") rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto -ldl") */
31
32 /* $NoPedantic */
33 /* $CopyInstall: conf/key.pem $(CONPATH) -m 0400 -o $(INSTUID) */
34 /* $CopyInstall: conf/cert.pem $(CONPATH) -m 0444 */
35
36
37 enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
38
39 static bool SelfSigned = false;
40
41 char* get_error()
42 {
43         return ERR_error_string(ERR_get_error(), NULL);
44 }
45
46 static int error_callback(const char *str, size_t len, void *u);
47
48 /** Represents an SSL user's extra data
49  */
50 class issl_session
51 {
52 public:
53         SSL* sess;
54         issl_status status;
55         reference<ssl_cert> cert;
56
57         int fd;
58         bool outbound;
59         bool data_to_write;
60
61         issl_session()
62         {
63                 outbound = false;
64                 data_to_write = false;
65         }
66 };
67
68 static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
69 {
70         /* XXX: This will allow self signed certificates.
71          * In the future if we want an option to not allow this,
72          * we can just return preverify_ok here, and openssl
73          * will boot off self-signed and invalid peer certs.
74          */
75         int ve = X509_STORE_CTX_get_error(ctx);
76
77         SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
78
79         return 1;
80 }
81
82 class ModuleSSLOpenSSL : public Module
83 {
84         int inbufsize;
85         issl_session* sessions;
86
87         SSL_CTX* ctx;
88         SSL_CTX* clictx;
89
90         char cipher[MAXBUF];
91
92         std::string keyfile;
93         std::string certfile;
94         std::string cafile;
95         // std::string crlfile;
96         std::string dhfile;
97         std::string sslports;
98
99         ServiceProvider iohook;
100  public:
101
102         ModuleSSLOpenSSL() : iohook(this, "ssl/openssl", SERVICE_IOHOOK)
103         {
104                 sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
105
106                 // Not rehashable...because I cba to reduce all the sizes of existing buffers.
107                 inbufsize = ServerInstance->Config->NetBufferSize;
108
109                 /* Global SSL library initialization*/
110                 SSL_library_init();
111                 SSL_load_error_strings();
112
113                 /* Build our SSL contexts:
114                  * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
115                  */
116                 ctx = SSL_CTX_new( SSLv23_server_method() );
117                 clictx = SSL_CTX_new( SSLv23_client_method() );
118
119                 SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
120                 SSL_CTX_set_mode(clictx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
121
122                 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
123                 SSL_CTX_set_verify(clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
124         }
125
126         void init()
127         {
128                 // Needs the flag as it ignores a plain /rehash
129                 OnModuleRehash(NULL,"ssl");
130                 Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnHookIO, I_OnUserConnect };
131                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
132                 ServerInstance->Modules->AddService(iohook);
133         }
134
135         void OnHookIO(StreamSocket* user, ListenSocket* lsb)
136         {
137                 if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "openssl")
138                 {
139                         /* Hook the user with our module */
140                         user->AddIOHook(this);
141                 }
142         }
143
144         void OnRehash(User* user)
145         {
146                 ConfigReader Conf;
147
148                 sslports.clear();
149
150                 for (size_t i = 0; i < ServerInstance->ports.size(); i++)
151                 {
152                         ListenSocket* port = ServerInstance->ports[i];
153                         if (port->bind_tag->getString("ssl") != "openssl")
154                                 continue;
155
156                         std::string portid = port->bind_desc;
157                         ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %s", portid.c_str());
158                         if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
159                                 sslports.append(portid).append(";");
160                 }
161
162                 if (!sslports.empty())
163                         sslports.erase(sslports.end() - 1);
164         }
165
166         void OnModuleRehash(User* user, const std::string &param)
167         {
168                 if (param != "ssl")
169                         return;
170
171                 OnRehash(user);
172
173                 ConfigReader Conf;
174
175                 cafile   = Conf.ReadValue("openssl", "cafile", 0);
176                 certfile = Conf.ReadValue("openssl", "certfile", 0);
177                 keyfile  = Conf.ReadValue("openssl", "keyfile", 0);
178                 dhfile   = Conf.ReadValue("openssl", "dhfile", 0);
179
180                 // Set all the default values needed.
181                 if (cafile.empty())
182                         cafile = "conf/ca.pem";
183
184                 if (certfile.empty())
185                         certfile = "conf/cert.pem";
186
187                 if (keyfile.empty())
188                         keyfile = "conf/key.pem";
189
190                 if (dhfile.empty())
191                         dhfile = "conf/dhparams.pem";
192
193                 /* Load our keys and certificates
194                  * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
195                  */
196                 if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
197                 {
198                         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
199                         ERR_print_errors_cb(error_callback, this);
200                 }
201
202                 if (((!SSL_CTX_use_PrivateKey_file(ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) || (!SSL_CTX_use_PrivateKey_file(clictx, keyfile.c_str(), SSL_FILETYPE_PEM)))
203                 {
204                         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
205                         ERR_print_errors_cb(error_callback, this);
206                 }
207
208                 /* Load the CAs we trust*/
209                 if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
210                 {
211                         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read CA list from %s. %s", cafile.c_str(), strerror(errno));
212                         ERR_print_errors_cb(error_callback, this);
213                 }
214
215                 FILE* dhpfile = fopen(dhfile.c_str(), "r");
216                 DH* ret;
217
218                 if (dhpfile == NULL)
219                 {
220                         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno));
221                         throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno));
222                 }
223                 else
224                 {
225                         ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
226                         if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
227                         {
228                                 ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
229                                 ERR_print_errors_cb(error_callback, this);
230                         }
231                 }
232
233                 fclose(dhpfile);
234         }
235
236         void On005Numeric(std::string &output)
237         {
238                 if (!sslports.empty())
239                         output.append(" SSL=" + sslports);
240         }
241
242         ~ModuleSSLOpenSSL()
243         {
244                 SSL_CTX_free(ctx);
245                 SSL_CTX_free(clictx);
246                 delete[] sessions;
247         }
248
249         void OnUserConnect(LocalUser* user)
250         {
251                 if (user->eh.GetIOHook() == this)
252                 {
253                         if (sessions[user->GetFd()].sess)
254                         {
255                                 SSLCertSubmission(user, this, ServerInstance->Modules->Find("m_sslinfo.so"), sessions[user->GetFd()].cert);
256                         }
257                 }
258         }
259
260         void OnCleanup(int target_type, void* item)
261         {
262                 if (target_type == TYPE_USER)
263                 {
264                         LocalUser* user = IS_LOCAL((User*)item);
265
266                         if (user && user->eh.GetIOHook() == this)
267                         {
268                                 // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
269                                 // Potentially there could be multiple SSL modules loaded at once on different ports.
270                                 ServerInstance->Users->QuitUser(user, "SSL module unloading");
271                         }
272                 }
273         }
274
275         Version GetVersion()
276         {
277                 return Version("Provides SSL support for clients", VF_VENDOR);
278         }
279
280         void OnRequest(Request& request)
281         {
282                 if (strcmp("GET_SSL_CERT", request.id) == 0)
283                 {
284                         SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
285                         int fd = req.sock->GetFd();
286                         issl_session* session = &sessions[fd];
287
288                         req.cert = session->cert;
289                 }
290         }
291
292         void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
293         {
294                 int fd = user->GetFd();
295
296                 issl_session* session = &sessions[fd];
297
298                 session->fd = fd;
299                 session->sess = SSL_new(ctx);
300                 session->status = ISSL_NONE;
301                 session->outbound = false;
302
303                 if (session->sess == NULL)
304                         return;
305
306                 if (SSL_set_fd(session->sess, fd) == 0)
307                 {
308                         ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
309                         return;
310                 }
311
312                 Handshake(user, session);
313         }
314
315         void OnStreamSocketConnect(StreamSocket* user)
316         {
317                 int fd = user->GetFd();
318                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
319                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() -1))
320                         return;
321
322                 issl_session* session = &sessions[fd];
323
324                 session->fd = fd;
325                 session->sess = SSL_new(clictx);
326                 session->status = ISSL_NONE;
327                 session->outbound = true;
328
329                 if (session->sess == NULL)
330                         return;
331
332                 if (SSL_set_fd(session->sess, fd) == 0)
333                 {
334                         ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
335                         return;
336                 }
337
338                 Handshake(user, session);
339         }
340
341         void OnStreamSocketClose(StreamSocket* user)
342         {
343                 int fd = user->GetFd();
344                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
345                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
346                         return;
347
348                 CloseSession(&sessions[fd]);
349         }
350
351         int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
352         {
353                 int fd = user->GetFd();
354                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
355                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
356                         return -1;
357
358                 issl_session* session = &sessions[fd];
359
360                 if (!session->sess)
361                 {
362                         CloseSession(session);
363                         return -1;
364                 }
365
366                 if (session->status == ISSL_HANDSHAKING)
367                 {
368                         // The handshake isn't finished and it wants to read, try to finish it.
369                         if (!Handshake(user, session))
370                         {
371                                 // Couldn't resume handshake.
372                                 if (session->status == ISSL_NONE)
373                                         return -1;
374                                 return 0;
375                         }
376                 }
377
378                 // If we resumed the handshake then session->status will be ISSL_OPEN
379
380                 if (session->status == ISSL_OPEN)
381                 {
382                         char* buffer = ServerInstance->GetReadBuffer();
383                         size_t bufsiz = ServerInstance->Config->NetBufferSize;
384                         int ret = SSL_read(session->sess, buffer, bufsiz);
385                         
386                         if (ret > 0)
387                         {
388                                 recvq.append(buffer, ret);
389                                 return 1;
390                         }
391                         else if (ret == 0)
392                         {
393                                 // Client closed connection.
394                                 CloseSession(session);
395                                 return -1;
396                         }
397                         else if (ret < 0)
398                         {
399                                 int err = SSL_get_error(session->sess, ret);
400
401                                 if (err == SSL_ERROR_WANT_READ)
402                                 {
403                                         ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ);
404                                         return 0;
405                                 }
406                                 else if (err == SSL_ERROR_WANT_WRITE)
407                                 {
408                                         ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
409                                         return 0;
410                                 }
411                                 else
412                                 {
413                                         CloseSession(session);
414                                         return -1;
415                                 }
416                         }
417                 }
418
419                 return 0;
420         }
421
422         int OnStreamSocketWrite(StreamSocket* user, std::string& buffer)
423         {
424                 int fd = user->GetFd();
425
426                 issl_session* session = &sessions[fd];
427
428                 if (!session->sess)
429                 {
430                         CloseSession(session);
431                         return -1;
432                 }
433
434                 session->data_to_write = true;
435
436                 if (session->status == ISSL_HANDSHAKING)
437                 {
438                         if (!Handshake(user, session))
439                         {
440                                 // Couldn't resume handshake.
441                                 if (session->status == ISSL_NONE)
442                                         return -1;
443                                 return 0;
444                         }
445                 }
446
447                 if (session->status == ISSL_OPEN)
448                 {
449                         int ret = SSL_write(session->sess, buffer.data(), buffer.size());
450                         if (ret == (int)buffer.length())
451                         {
452                                 session->data_to_write = false;
453                                 ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
454                                 return 1;
455                         }
456                         else if (ret > 0)
457                         {
458                                 buffer = buffer.substr(ret);
459                                 ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
460                                 return 0;
461                         }
462                         else if (ret == 0)
463                         {
464                                 CloseSession(session);
465                                 return -1;
466                         }
467                         else if (ret < 0)
468                         {
469                                 int err = SSL_get_error(session->sess, ret);
470
471                                 if (err == SSL_ERROR_WANT_WRITE)
472                                 {
473                                         ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
474                                         return 0;
475                                 }
476                                 else if (err == SSL_ERROR_WANT_READ)
477                                 {
478                                         ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
479                                         return 0;
480                                 }
481                                 else
482                                 {
483                                         CloseSession(session);
484                                         return -1;
485                                 }
486                         }
487                 }
488                 return 0;
489         }
490
491         bool Handshake(StreamSocket* user, issl_session* session)
492         {
493                 int ret;
494
495                 if (session->outbound)
496                         ret = SSL_connect(session->sess);
497                 else
498                         ret = SSL_accept(session->sess);
499
500                 if (ret < 0)
501                 {
502                         int err = SSL_get_error(session->sess, ret);
503
504                         if (err == SSL_ERROR_WANT_READ)
505                         {
506                                 ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
507                                 session->status = ISSL_HANDSHAKING;
508                                 return true;
509                         }
510                         else if (err == SSL_ERROR_WANT_WRITE)
511                         {
512                                 ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
513                                 session->status = ISSL_HANDSHAKING;
514                                 return true;
515                         }
516                         else
517                         {
518                                 CloseSession(session);
519                         }
520
521                         return false;
522                 }
523                 else if (ret > 0)
524                 {
525                         // Handshake complete.
526                         VerifyCertificate(session, user);
527
528                         session->status = ISSL_OPEN;
529
530                         ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
531
532                         return true;
533                 }
534                 else if (ret == 0)
535                 {
536                         CloseSession(session);
537                         return true;
538                 }
539
540                 return true;
541         }
542
543         void CloseSession(issl_session* session)
544         {
545                 if (session->sess)
546                 {
547                         SSL_shutdown(session->sess);
548                         SSL_free(session->sess);
549                 }
550
551                 session->sess = NULL;
552                 session->status = ISSL_NONE;
553                 errno = EIO;
554         }
555
556         void VerifyCertificate(issl_session* session, StreamSocket* user)
557         {
558                 if (!session->sess || !user || session->cert)
559                         return;
560
561                 X509* cert;
562                 ssl_cert* certinfo = new ssl_cert;
563                 session->cert = certinfo;
564                 unsigned int n;
565                 unsigned char md[EVP_MAX_MD_SIZE];
566                 const EVP_MD *digest = EVP_md5();
567
568                 cert = SSL_get_peer_certificate((SSL*)session->sess);
569
570                 if (!cert)
571                 {
572                         certinfo->error = "Could not get peer certificate: "+std::string(get_error());
573                         return;
574                 }
575
576                 certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK);
577
578                 if (SelfSigned)
579                 {
580                         certinfo->unknownsigner = false;
581                         certinfo->trusted = true;
582                 }
583                 else
584                 {
585                         certinfo->unknownsigner = true;
586                         certinfo->trusted = false;
587                 }
588
589                 certinfo->dn = X509_NAME_oneline(X509_get_subject_name(cert),0,0);
590                 certinfo->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
591
592                 if (!X509_digest(cert, digest, md, &n))
593                 {
594                         certinfo->error = "Out of memory generating fingerprint";
595                 }
596                 else
597                 {
598                         certinfo->fingerprint = irc::hex(md, n);
599                 }
600
601                 if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), ServerInstance->Time()) == 0))
602                 {
603                         certinfo->error = "Not activated, or expired certificate";
604                 }
605
606                 X509_free(cert);
607         }
608 };
609
610 static int error_callback(const char *str, size_t len, void *u)
611 {
612         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "SSL error: " + std::string(str, len - 1));
613
614         //
615         // XXX: Remove this line, it causes valgrind warnings...
616         //
617         // MD_update(&m, buf, j);
618         //
619         //
620         // ... ONLY JOKING! :-)
621         //
622
623         return 0;
624 }
625
626 MODULE_INIT(ModuleSSLOpenSSL)