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