1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2008 Jeff Davey. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         author:         Jeff Davey <j@submersion.com>
8 
9 *******************************************************************************/
10 
11 module tango.net.device.SSLSocket;
12 
13 private import tango.net.util.PKI;
14 
15 private import tango.net.device.Socket;
16 
17 private import tango.net.device.Berkeley;
18 
19 private import tango.net.util.c.OpenSSL;
20 
21 /*******************************************************************************
22 
23     SSLSocket is a sub-class of Socket. It's purpose is to
24     provide SSL encryption at the socket level as well as easily fit into
25     existing Tango network applications that may already be using Socket.
26 
27     SSLSocket requires the OpenSSL library, and uses a dynamic binding
28     to the library. You can find the library at http://www.openssl.org and a
29     Win32 specific port at http://www.slproweb.com/products/Win32OpenSSL.html.
30 
31     SSLSockets have two modes:
32 
33     1. Client mode, useful for connecting to existing servers, but not
34     accepting new connections. Accepting a new connection will cause
35     the library to stall on a write on connection.
36 
37     2. Server mode, useful for creating an SSL server, but not connecting
38     to an existing server. Connection will cause the library to stall on a
39     read on connection.
40 
41     Example SSL client
42     ---
43     auto s = new SSLSocket;
44     if (s.connect("www.yahoo.com", 443))
45     {
46         char[1024] buff;
47 
48         s.write("GET / HTTP/1.0\r\n\r\n");
49         auto bytesRead = s.read(buff);
50         if (bytesRead != s.Eof)
51             Stdout.formatln("received: {}", buff[0..bytesRead]);
52     }
53     ---
54 
55 *******************************************************************************/
56 
57 class SSLSocket : Socket
58 {
59     protected BIO *sslSocket = null;
60     protected SSLCtx sslCtx = null;
61 /+
62     private bool timeout;
63     private SocketSet readSet;
64     private SocketSet writeSet;
65 +/
66     /*******************************************************************************
67 
68         Create a default Client Mode SSLSocket.
69 
70     *******************************************************************************/
71 
72     this (bool config = true)
73     {
74         super();
75 
76         if (config)
77             setCtx (new SSLCtx, true);
78     }
79 
80 /+
81     /*******************************************************************************
82 
83         Creates a Client Mode SSLSocket
84 
85         This is overriding the Socket ctor in order to emulate the
86         existing free-list frameowrk.
87 
88         Specifying anything other than ProtocolType.TCP or SocketType.STREAM will
89         cause an Exception to be thrown.
90 
91     *******************************************************************************/
92 
93     override this(SocketType type, ProtocolType protocol)
94     {
95         if (protocol != ProtocolType.TCP)
96             throw new Exception("SSL is only supported over TCP.");
97         if (type != SocketType.STREAM)
98             throw new Exception("SSL is only supporting with streaming types.");
99         super(AddressFamily.INET, type, protocol); // hardcoding this to INET for now
100         //if (create)
101         {
102             sslCtx = new SSLCtx();
103             sslSocket = _convertToSSL(sslCtx, false, true);
104         }
105     }
106 
107     /*******************************************************************************
108 
109         Creates a SSLSocket
110 
111         This class allows the ability to turn a regular Socket into an
112         SSLSocket. It also gives the ability to change an SSLSocket
113         into Server Mode or ClientMode.
114 
115         Params:
116             sock = The socket to wrap in SSL
117             SSLCtx = the SSL Context as provided by the PKI layer.
118             clientMode = if true the socket will be Client Mode, Server otherwise.
119 
120     *******************************************************************************/
121 
122 
123     this(Socket sock, SSLCtx ctx, bool clientMode = true)
124     {
125         super(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP, false); // hardcoding to inet now
126         socket_ = sock;
127         sslCtx = ctx;
128         sslSocket = _convertToSSL(sslCtx, false, clientMode);
129     }
130 
131     ~this()
132     {
133         if (sslSocket)
134         {
135             BIO_reset(sslSocket);
136             BIO_free_all(sslSocket);
137             sslSocket = null;
138         }
139     }
140 +/
141 
142     /*******************************************************************************
143 
144         Release this SSLSocket.
145 
146         As per Socket.detach.
147 
148     *******************************************************************************/
149 
150     override void detach()
151     {
152         if (sslSocket)
153         {
154             BIO_reset(sslSocket);
155             BIO_free_all(sslSocket);
156             sslSocket = null;
157         }
158         super.detach();
159     }
160 
161     /*******************************************************************************
162 
163         Writes the passed buffer to the underlying socket stream. This will
164         block until socket error.
165 
166         As per Socket.write
167 
168     *******************************************************************************/
169 
170     override size_t write(const(void)[] src)
171     {
172         if (src.length is 0)
173             return 0;
174 
175         int bytes = BIO_write(sslSocket, cast(void*)src.ptr, cast(uint)src.length);
176         if (bytes <= 0)
177             return Eof;
178         return cast(size_t) bytes;
179     }
180 
181     /*******************************************************************************
182 
183          Reads from the underlying socket stream. If needed, setTimeout will
184         set the max length of time the read will take before returning.
185 
186         As per Socket.read
187 
188     *******************************************************************************/
189 
190 
191     override size_t read(void[] dst)
192     {
193 /+
194         timeout = false;
195         if (tv.tv_usec | tv.tv_sec)
196         {
197             size_t rtn = Eof;
198             // need to switch to nonblocking...
199             bool blocking = socket_.blocking;
200             if (blocking) socket_.blocking = false;
201             do
202             {
203                 int bytesRead = BIO_read(sslSocket, dst.ptr, dst.length);
204                 if (bytesRead <= 0)
205                 {
206                     bool read = false;
207                     bool write = false;
208                     if (!BIO_should_retry(sslSocket))
209                         break;
210                     if (BIO_should_read(sslSocket))
211                         read = true;
212                     if (BIO_should_write(sslSocket))
213                         write = true;
214                     if (read || write)
215                     {
216                         if (read)
217                         {
218                             if (readSet is null)
219                                 readSet = new SocketSet(1);
220                             readSet.reset();
221                             readSet.add(socket_);
222                         }
223                         if (write)
224                         {
225                             if (writeSet is null)
226                                 writeSet = new SocketSet(1);
227                             writeSet.reset();
228                             writeSet.add(socket_);
229                         }
230                         auto copy = tv;
231                         int i = socket_.select(read ? readSet : null, write ? writeSet : null, null, &copy);
232                         if (i <= 0)
233                         {
234                             if (i is 0)
235                                 timeout = true;
236                             break;
237                         }
238                     }
239                     else if (BIO_should_io_special(sslSocket)) // wasn't write, wasn't read.. something "special" just wait for the socket to become ready...
240                         Thread.sleep(.05);
241                     else
242                         break;
243                 }
244                 else
245                 {
246                     rtn = bytesRead;
247                     break;
248                 }
249             } while(BIO_should_retry(sslSocket));
250             if (blocking) socket_.blocking = blocking;
251             return rtn;
252         }
253 +/
254         int bytes = BIO_read(sslSocket, dst.ptr, cast(uint)dst.length);
255         if (bytes <= 0)
256             return Eof;
257         return cast(size_t) bytes;
258     }
259 
260     /*******************************************************************************
261 
262         Shuts down the underlying socket for reading and writing.
263 
264         As per Socket.shutdown
265 
266     *******************************************************************************/
267 
268     override SSLSocket shutdown()
269     {
270         SSL *obj;
271         BIO_get_ssl(sslSocket, &obj);
272         if (obj)
273         {
274             if (!SSL_get_shutdown)
275                 SSL_set_shutdown(obj, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
276         }
277         return this;
278     }
279 
280     /*******************************************************************************
281 
282         Used in conjuction with the above ctor with the create flag disabled. It is
283         useful for accepting a new socket into a SSLSocket, and then re-using
284         the Server's existing SSLCtx.
285 
286         Params:
287             ctx = SSLCtx class as provided by PKI
288             clientMode = if true, the socket will be in Client Mode, Server otherwise.
289 
290     *******************************************************************************/
291 
292 
293     void setCtx(SSLCtx ctx, bool clientMode = true)
294     {
295         sslCtx = ctx;
296         sslSocket = _convertToSSL(sslCtx, false, clientMode);
297     }
298 
299     /*
300         Converts an existing socket (should be TCP) to an "SSL" socket
301         close = close the socket when finished -- should probably be false usually
302         client = if true, "client-mode" if false "server-mode"
303     */
304     private BIO *_convertToSSL(SSLCtx sslCtx, bool close, bool client)
305     {
306         BIO *rtn = null;
307 
308         BIO *socketBio = BIO_new_socket(native.handle, close ? BIO_CLOSE : BIO_NOCLOSE);
309         if (socketBio)
310         {
311             rtn = BIO_new_ssl(sslCtx.native, client);
312             if (rtn)
313                 rtn = BIO_push(rtn, socketBio);
314             if (!rtn)
315                 BIO_free_all(socketBio);
316         }
317 
318         if (rtn is null)
319             throwOpenSSLError();
320         return rtn;
321     }
322 }
323 
324 
325 /*******************************************************************************
326 
327     SSLServerSocket is a sub-class of ServerSocket. It's purpose is to provide
328     SSL encryption at the socket level as well as easily tie into existing
329     Tango applications that may already be using ServerSocket.
330 
331     SSLServerSocket requires the OpenSSL library, and uses a dynamic binding
332     to the library. You can find the library at http://www.openssl.org and a
333     Win32 specific port at http://www.slproweb.com/products/Win32OpenSSL.html.
334 
335     Example SSL server
336     ---
337     auto cert = new Certificate(cast(char[])File.get("public.pem"));
338     auto pkey = new PrivateKey(cast(char[])File.get("private.pem"));
339     auto ctx = new SSLCtx;
340     ctx.certificate(cert).privateKey(pkey);
341     auto server = new SSLServerSocket(443, ctx);
342     for(;;)
343     {
344         auto sc = server.accept;
345         sc.write("HTTP/1.1 200\r\n\r\n<b>Hello World</b>");
346         sc.shutdown.close;
347     }
348     ---
349 
350 *******************************************************************************/
351 
352 class SSLServerSocket : ServerSocket
353 {
354     private SSLCtx sslCtx;
355 
356     /*******************************************************************************
357 
358     *******************************************************************************/
359 
360     this (ushort port, SSLCtx ctx, int backlog=32, bool reuse=false)
361     {
362         scope addr = new IPv4Address (port);
363         this (addr, ctx, backlog, reuse);
364     }
365 
366     /*******************************************************************************
367 
368         Constructs a new SSLServerSocket. This constructor is similar to
369         ServerSocket, except it takes a SSLCtx as provided by PKI.
370 
371         Params:
372             addr = the address to bind and listen on.
373             ctx = the provided SSLCtx
374             backlog = the number of connections to backlog before refusing connection
375             reuse = if enabled, allow rebinding of existing ip/port
376 
377     *******************************************************************************/
378 
379     this(Address addr, SSLCtx ctx, int backlog=32, bool reuse=false)
380     {
381         super(addr, backlog, reuse);
382         sslCtx = ctx;
383     }
384 
385     alias ServerSocket.accept accept;
386 
387     /*******************************************************************************
388 
389       Accepts a new connection and copies the provided server SSLCtx to a new
390       SSLSocket.
391 
392     *******************************************************************************/
393 
394     SSLSocket accept (SSLSocket recipient = null)
395     {
396         if (recipient is null)
397             recipient = new SSLSocket(false);
398 
399         super.accept (recipient);
400         recipient.setCtx(sslCtx, false);
401         return recipient;
402     }
403 }
404 
405 
406 
407 
408 
409 version(Test)
410 {
411     import tetra.util.Test;
412     import tango.io.Stdout;
413     import tango.io.device.File;
414     import tango.io.FilePath;
415     import tango.core.Thread;
416     import tango.stdc.stringz;
417 
418     extern (C)
419     {
420         int blah(int booger, void *x)
421         {
422             return 1;
423         }
424     }
425 
426 
427     unittest
428     {
429         auto t2 = 1.0;
430         loadOpenSSL();
431         Test.Status sslCTXTest(ref char[][] messages)
432         {
433             auto s1 = new SSLSocket();
434             if (s1)
435             {
436                 bool good = false;
437                 try
438                     auto s2 = new SSLSocket(SocketType.STREAM,  ProtocolType.UDP);
439                 catch (Exception e)
440                     good = true;
441 
442                 if (good)
443                 {
444                     Socket mySock = new Socket(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP);
445                     if (mySock)
446                     {
447                         Certificate publicCertificate;
448                         PrivateKey privateKey;
449                         try
450                         {
451                             publicCertificate = new Certificate(cast(char[])File.get ("public.pem"));
452                             privateKey = new PrivateKey(cast(char[])File.get ("private.pem"));
453                         }
454                         catch (Exception ex)
455                         {
456                             privateKey = new PrivateKey(2048);
457                             publicCertificate = new Certificate();
458                             publicCertificate.privateKey(privateKey).serialNumber(123).dateBeforeOffset(t1).dateAfterOffset(t2);
459                             publicCertificate.setSubject("CA", "Alberta", "Place", "None", "First Last", "no unit", "email@example.com").sign(publicCertificate, privateKey);
460                         }
461                         auto sslCtx = new SSLCtx();
462                         sslCtx.certificate(publicCertificate).privateKey(privateKey).checkKey();
463                         auto s3 = new SSLSocket(mySock, sslCtx);
464                         if (s3)
465                             return Test.Status.Success;
466                     }
467                 }
468             }
469             return Test.Status.Failure;
470         }
471 
472         Test.Status sslReadWriteTest(ref char[][] messages)
473         {
474             auto s1 = new SSLSocket();
475             auto address = new IPv4Address("209.20.65.224", 443);
476             if (s1.connect(address))
477             {
478                 char[] command = "GET /result.txt\r\n";
479                 s1.write(command);
480                 char[1024] result;
481                 uint bytesRead = s1.read(result);
482                 if (bytesRead > 0 && bytesRead != Eof && (result[0 .. bytesRead] == "I got results!\n"))
483                     return Test.Status.Success;
484                 else
485                     messages ~= Stdout.layout()("Received wrong results: (bytesRead: {}), (result: {})", bytesRead, result[0..bytesRead]);
486             }
487             return Test.Status.Failure;
488         }
489 
490         Test.Status sslReadWriteTestWithTimeout(ref char[][] messages)
491         {
492             auto s1 = new SSLSocket();
493             auto address = new IPv4Address("209.20.65.224", 443);
494             if (s1.connect(address))
495             {
496                 char[] command = "GET /result.txt HTTP/1.1\r\nHost: submersion.com\r\n\r\n";
497                 s1.write(command);
498                 char[1024] result;
499                 uint bytesRead = s1.read(result);
500                 char[] expectedResult = "HTTP/1.1 200 OK";
501                 if (bytesRead > 0 && bytesRead != Eof && (result[0 .. expectedResult.length] == expectedResult))
502                 {
503                     s1.setTimeout(t2);
504                     while (bytesRead != s1.Eof)
505                         bytesRead = s1.read(result);
506                     if (s1.hadTimeout)
507                         return Test.Status.Success;
508                     else
509                         messages ~= Stdout.layout()("Did not get timeout on read: {}", bytesRead);
510                 }
511                 else
512                     messages ~= Stdout.layout()("Received wrong results: (bytesRead: {}), (result: {})", bytesRead, result[0..bytesRead]);
513             }
514             return Test.Status.Failure;
515         }
516 
517         auto t = new Test("tetra.net.SSLSocket");
518         t["SSL_CTX"] = &sslCTXTest;
519         t["Read/Write"] = &sslReadWriteTest;
520         t["Read/Write Timeout"] = &sslReadWriteTestWithTimeout;
521         t.run();
522     }
523 }