Merge pull request #1818 from dibdot/lxc_fix
[project/luci.git] / libs / luci-lib-nixio / axTLS / samples / lua / axssl.lua
1 #!/usr/local/bin/lua
2
3 --
4 -- Copyright (c) 2007, Cameron Rich
5 --
6 -- All rights reserved.
7 --
8 -- Redistribution and use in source and binary forms, with or without
9 -- modification, are permitted provided that the following conditions are met:
10 --
11 -- * Redistributions of source code must retain the above copyright notice,
12 --   this list of conditions and the following disclaimer.
13 -- * Redistributions in binary form must reproduce the above copyright
14 --   notice, this list of conditions and the following disclaimer in the
15 --   documentation and/or other materials provided with the distribution.
16 -- * Neither the name of the axTLS project nor the names of its
17 --   contributors may be used to endorse or promote products derived
18 --   from this software without specific prior written permission.
19 --
20 -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 -- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 -- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
23 -- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 -- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 -- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26 -- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 -- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
28 -- OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 -- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 -- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 --
32
33 --
34 -- Demonstrate the use of the axTLS library in Lua with a set of 
35 -- command-line parameters similar to openssl. In fact, openssl clients 
36 -- should be able to communicate with axTLS servers and visa-versa.
37 --
38 -- This code has various bits enabled depending on the configuration. To enable
39 -- the most interesting version, compile with the 'full mode' enabled.
40 --
41 -- To see what options you have, run the following:
42 -- > [lua] axssl s_server -?
43 -- > [lua] axssl s_client -?
44 --
45 -- The axtls/axtlsl shared libraries must be in the same directory or be found 
46 -- by the OS.
47 --
48 --
49 require "bit"
50 require("axtlsl")
51 local socket = require("socket")
52
53 -- print version?
54 if #arg == 1 and arg[1] == "version" then
55     print("axssl.lua "..axtlsl.ssl_version())
56     os.exit(1)
57 end
58
59 --
60 -- We've had some sort of command-line error. Print out the basic options.
61 --
62 function print_options(option)
63     print("axssl: Error: '"..option.."' is an invalid command.")
64     print("usage: axssl [s_server|s_client|version] [args ...]")
65     os.exit(1)
66 end
67
68 --
69 -- We've had some sort of command-line error. Print out the server options.
70 --
71 function print_server_options(build_mode, option)
72     local cert_size = axtlsl.ssl_get_config(axtlsl.SSL_MAX_CERT_CFG_OFFSET)
73     local ca_cert_size = axtlsl.ssl_get_config(
74             axtlsl.SSL_MAX_CA_CERT_CFG_OFFSET)
75
76     print("unknown option "..option)
77     print("usage: s_server [args ...]")
78     print(" -accept\t- port to accept on (default is 4433)")
79     print(" -quiet\t\t- No server output")
80
81     if build_mode >= axtlsl.SSL_BUILD_SERVER_ONLY then
82         print(" -cert arg\t- certificate file to add (in addition to "..
83                 "default) to chain -")
84         print("\t\t  Can repeat up to "..cert_size.." times")
85         print(" -key arg\t- Private key file to use - default DER format")
86         print(" -pass\t\t- private key file pass phrase source")
87     end
88
89     if build_mode >= axtlsl.SSL_BUILD_ENABLE_VERIFICATION then
90         print(" -verify\t- turn on peer certificate verification")
91         print(" -CAfile arg\t- Certificate authority - default DER format")
92         print("\t\t  Can repeat up to "..ca_cert_size.." times")
93     end
94
95     if build_mode == axtlsl.SSL_BUILD_FULL_MODE then
96         print(" -debug\t\t- Print more output")
97         print(" -state\t\t- Show state messages")
98         print(" -show-rsa\t- Show RSA state")
99     end
100
101     os.exit(1)
102 end
103
104 --
105 -- We've had some sort of command-line error. Print out the client options.
106 --
107 function print_client_options(build_mode, option)
108     local cert_size = axtlsl.ssl_get_config(axtlsl.SSL_MAX_CERT_CFG_OFFSET)
109     local ca_cert_size = axtlsl.ssl_get_config(
110             axtlsl.SSL_MAX_CA_CERT_CFG_OFFSET)
111
112     print("unknown option "..option)
113
114     if build_mode >= axtlsl.SSL_BUILD_ENABLE_CLIENT then
115         print("usage: s_client [args ...]")
116         print(" -connect host:port - who to connect to (default "..
117                 "is localhost:4433)")
118         print(" -verify\t- turn on peer certificate verification")
119         print(" -cert arg\t- certificate file to use - default DER format")
120         print(" -key arg\t- Private key file to use - default DER format")
121         print("\t\t  Can repeat up to "..cert_size.." times")
122         print(" -CAfile arg\t- Certificate authority - default DER format")
123         print("\t\t  Can repeat up to "..ca_cert_size.."times")
124         print(" -quiet\t\t- No client output")
125         print(" -pass\t\t- private key file pass phrase source")
126         print(" -reconnect\t- Drop and re-make the connection "..
127                 "with the same Session-ID")
128
129         if build_mode == axtlsl.SSL_BUILD_FULL_MODE then
130             print(" -debug\t\t- Print more output")
131             print(" -state\t\t- Show state messages")
132             print(" -show-rsa\t- Show RSA state")
133         end
134     else
135         print("Change configuration to allow this feature")
136     end
137
138     os.exit(1)
139 end
140
141 -- Implement the SSL server logic. 
142 function do_server(build_mode)
143     local i = 2
144     local v
145     local port = 4433
146     local options = axtlsl.SSL_DISPLAY_CERTS
147     local quiet = false
148     local password = ""
149     local private_key_file = nil
150     local cert_size = axtlsl.ssl_get_config(axtlsl.SSL_MAX_CERT_CFG_OFFSET)
151     local ca_cert_size = axtlsl.
152                             ssl_get_config(axtlsl.SSL_MAX_CA_CERT_CFG_OFFSET)
153     local cert = {}
154     local ca_cert = {}
155
156     while i <= #arg do
157         if arg[i] ==  "-accept" then
158             if i >= #arg then
159                 print_server_options(build_mode, arg[i])
160             end
161
162             i = i + 1
163             port = arg[i]
164         elseif arg[i] == "-quiet" then
165             quiet = true
166             options = bit.band(options, bit.bnot(axtlsl.SSL_DISPLAY_CERTS))
167         elseif build_mode >= axtlsl.SSL_BUILD_SERVER_ONLY then
168             if arg[i] == "-cert" then
169                 if i >= #arg or #cert >= cert_size then
170                     print_server_options(build_mode, arg[i]) 
171                 end
172
173                 i = i + 1
174                 table.insert(cert, arg[i])
175             elseif arg[i] == "-key" then
176                 if i >= #arg then
177                     print_server_options(build_mode, arg[i]) 
178                 end
179
180                 i = i + 1
181                 private_key_file = arg[i]
182                 options = bit.bor(options, axtlsl.SSL_NO_DEFAULT_KEY)
183             elseif arg[i] == "-pass" then
184                 if i >= #arg then
185                     print_server_options(build_mode, arg[i]) 
186                 end
187
188                 i = i + 1
189                 password = arg[i]
190             elseif build_mode >= axtlsl.SSL_BUILD_ENABLE_VERIFICATION then
191                 if arg[i] == "-verify" then
192                     options = bit.bor(options, axtlsl.SSL_CLIENT_AUTHENTICATION)
193                 elseif arg[i] == "-CAfile" then
194                     if i >= #arg or #ca_cert >= ca_cert_size then
195                         print_server_options(build_mode, arg[i])  
196                     end
197
198                     i = i + 1
199                     table.insert(ca_cert, arg[i])
200                 elseif build_mode == axtlsl.SSL_BUILD_FULL_MODE then
201                     if arg[i] == "-debug" then
202                         options = bit.bor(options, axtlsl.SSL_DISPLAY_BYTES)
203                     elseif arg[i] == "-state" then
204                         options = bit.bor(options, axtlsl.SSL_DISPLAY_STATES)
205                     elseif arg[i] == "-show-rsa" then
206                         options = bit.bor(options, axtlsl.SSL_DISPLAY_RSA)
207                     else
208                         print_server_options(build_mode, arg[i])
209                     end
210                 else
211                     print_server_options(build_mode, arg[i])
212                 end
213             else 
214                 print_server_options(build_mode, arg[i])
215             end
216         else 
217             print_server_options(build_mode, arg[i])
218         end
219
220         i = i + 1
221     end
222
223     -- Create socket for incoming connections
224     local server_sock = socket.try(socket.bind("*", port))
225
226     ---------------------------------------------------------------------------
227     -- This is where the interesting stuff happens. Up until now we've
228     -- just been setting up sockets etc. Now we do the SSL handshake.
229     ---------------------------------------------------------------------------
230     local ssl_ctx = axtlsl.ssl_ctx_new(options, axtlsl.SSL_DEFAULT_SVR_SESS)
231     if ssl_ctx == nil then error("Error: Server context is invalid") end
232
233     if private_key_file ~= nil then
234         local obj_type = axtlsl.SSL_OBJ_RSA_KEY
235
236         if string.find(private_key_file, ".p8") then 
237             obj_type = axtlsl.SSL_OBJ_PKCS8 
238         end
239
240         if string.find(private_key_file, ".p12") then 
241             obj_type = axtlsl.SSL_OBJ_PKCS12 
242         end
243
244         if axtlsl.ssl_obj_load(ssl_ctx, obj_type, private_key_file, 
245                                         password) ~= axtlsl.SSL_OK then
246             error("Private key '" .. private_key_file .. "' is undefined.")
247         end
248     end
249
250     for _, v in ipairs(cert) do
251         if axtlsl.ssl_obj_load(ssl_ctx, axtlsl.SSL_OBJ_X509_CERT, v, "") ~= 
252                                         axtlsl.SSL_OK then
253             error("Certificate '"..v .. "' is undefined.")
254         end
255     end
256
257     for _, v in ipairs(ca_cert) do
258         if axtlsl.ssl_obj_load(ssl_ctx, axtlsl.SSL_OBJ_X509_CACERT, v, "") ~= 
259                                         axtlsl.SSL_OK then
260             error("Certificate '"..v .."' is undefined.")
261         end
262     end
263
264     while true do
265         if not quiet then print("ACCEPT") end
266         local client_sock = server_sock:accept();
267         local ssl = axtlsl.ssl_server_new(ssl_ctx, client_sock:getfd())
268
269         -- do the actual SSL handshake
270         local connected = false
271         local res
272         local buf
273
274         while true do
275             socket.select({client_sock}, nil)
276             res, buf = axtlsl.ssl_read(ssl)
277
278             if res == axtlsl.SSL_OK then -- connection established and ok
279                 if axtlsl.ssl_handshake_status(ssl) == axtlsl.SSL_OK then
280                     if not quiet and not connected then
281                         display_session_id(ssl)
282                         display_cipher(ssl)
283                     end
284                     connected = true
285                 end
286             end
287
288             if res > axtlsl.SSL_OK then
289                 for _, v in ipairs(buf) do
290                     io.write(string.format("%c", v))
291                 end
292             elseif res < axtlsl.SSL_OK then 
293                 if not quiet then
294                     axtlsl.ssl_display_error(res)
295                 end
296                 break
297             end
298         end
299
300         -- client was disconnected or the handshake failed.
301         print("CONNECTION CLOSED")
302         axtlsl.ssl_free(ssl)
303         client_sock:close()
304     end
305
306     axtlsl.ssl_ctx_free(ssl_ctx)
307 end
308
309 --
310 -- Implement the SSL client logic.
311 --
312 function do_client(build_mode)
313     local i = 2
314     local v
315     local port = 4433
316     local options = 
317             bit.bor(axtlsl.SSL_SERVER_VERIFY_LATER, axtlsl.SSL_DISPLAY_CERTS)
318     local private_key_file = nil
319     local reconnect = 0
320     local quiet = false
321     local password = ""
322     local session_id = {}
323     local host = "127.0.0.1"
324     local cert_size = axtlsl.ssl_get_config(axtlsl.SSL_MAX_CERT_CFG_OFFSET)
325     local ca_cert_size = axtlsl.
326                             ssl_get_config(axtlsl.SSL_MAX_CA_CERT_CFG_OFFSET)
327     local cert = {}
328     local ca_cert = {}
329
330     while i <= #arg do
331         if arg[i] == "-connect" then
332             if i >= #arg then
333                 print_client_options(build_mode, arg[i]) 
334             end
335
336             i = i + 1
337             local t = string.find(arg[i], ":")
338             host = string.sub(arg[i], 1, t-1)
339             port = string.sub(arg[i], t+1)
340         elseif arg[i] == "-cert" then
341             if i >= #arg or #cert >= cert_size then
342                 print_client_options(build_mode, arg[i]) 
343             end
344
345             i = i + 1
346             table.insert(cert, arg[i])
347         elseif arg[i] == "-key" then
348             if i >= #arg then
349                 print_client_options(build_mode, arg[i])
350             end
351
352             i = i + 1
353             private_key_file = arg[i]
354             options = bit.bor(options, axtlsl.SSL_NO_DEFAULT_KEY)
355         elseif arg[i] == "-CAfile" then
356             if i >= #arg or #ca_cert >= ca_cert_size then
357                 print_client_options(build_mode, arg[i]) 
358             end
359
360             i = i + 1
361             table.insert(ca_cert, arg[i])
362         elseif arg[i] == "-verify" then
363             options = bit.band(options, 
364                                 bit.bnot(axtlsl.SSL_SERVER_VERIFY_LATER))
365         elseif arg[i] == "-reconnect" then
366             reconnect = 4
367         elseif arg[i] == "-quiet" then
368             quiet = true
369             options = bit.band(options, bnot(axtlsl.SSL_DISPLAY_CERTS))
370         elseif arg[i] == "-pass" then
371             if i >= #arg then
372                 print_server_options(build_mode, arg[i])
373             end
374
375             i = i + 1
376             password = arg[i]
377         elseif build_mode == axtlsl.SSL_BUILD_FULL_MODE then
378             if arg[i] == "-debug" then
379                 options = bit.bor(options, axtlsl.SSL_DISPLAY_BYTES)
380             elseif arg[i] == "-state" then
381                 options = bit.bor(axtlsl.SSL_DISPLAY_STATES)
382             elseif arg[i] == "-show-rsa" then
383                 options = bit.bor(axtlsl.SSL_DISPLAY_RSA)
384             else    -- don't know what this is
385                 print_client_options(build_mode, arg[i])
386             end
387         else    -- don't know what this is
388             print_client_options(build_mode, arg[i])
389         end
390
391         i = i + 1
392     end
393
394     local client_sock = socket.try(socket.connect(host, port))
395     local ssl
396     local res
397
398     if not quiet then print("CONNECTED") end
399
400     ---------------------------------------------------------------------------
401     -- This is where the interesting stuff happens. Up until now we've
402     -- just been setting up sockets etc. Now we do the SSL handshake.
403     ---------------------------------------------------------------------------
404     local ssl_ctx = axtlsl.ssl_ctx_new(options, axtlsl.SSL_DEFAULT_CLNT_SESS)
405
406     if ssl_ctx == nil then 
407         error("Error: Client context is invalid")
408     end
409
410     if private_key_file ~= nil then
411         local obj_type = axtlsl.SSL_OBJ_RSA_KEY
412
413         if string.find(private_key_file, ".p8") then 
414             obj_type = axtlsl.SSL_OBJ_PKCS8 
415         end
416
417         if string.find(private_key_file, ".p12") then 
418             obj_type = axtlsl.SSL_OBJ_PKCS12 
419         end
420
421         if axtlsl.ssl_obj_load(ssl_ctx, obj_type, private_key_file, 
422                                         password) ~= axtlsl.SSL_OK then
423             error("Private key '"..private_key_file.."' is undefined.")
424         end
425     end
426
427     for _, v in ipairs(cert) do
428         if axtlsl.ssl_obj_load(ssl_ctx, axtlsl.SSL_OBJ_X509_CERT, v, "") ~= 
429                                         axtlsl.SSL_OK then
430             error("Certificate '"..v .. "' is undefined.")
431         end
432     end
433
434     for _, v in ipairs(ca_cert) do
435         if axtlsl.ssl_obj_load(ssl_ctx, axtlsl.SSL_OBJ_X509_CACERT, v, "") ~= 
436                                         axtlsl.SSL_OK then
437             error("Certificate '"..v .."' is undefined.")
438         end
439     end
440
441     -- Try session resumption?
442     if reconnect ~= 0 then
443         local session_id = nil
444         local sess_id_size = 0
445
446         while reconnect > 0 do
447             reconnect = reconnect - 1
448             ssl = axtlsl.ssl_client_new(ssl_ctx, 
449                     client_sock:getfd(), session_id, sess_id_size)
450
451             res = axtlsl.ssl_handshake_status(ssl)
452             if res ~= axtlsl.SSL_OK then
453                 if not quiet then axtlsl.ssl_display_error(res) end
454                 axtlsl.ssl_free(ssl)
455                 os.exit(1)
456             end
457
458             display_session_id(ssl)
459             session_id = axtlsl.ssl_get_session_id(ssl)
460             sess_id_size = axtlsl.ssl_get_session_id_size(ssl)
461
462             if reconnect > 0 then
463                 axtlsl.ssl_free(ssl)
464                 client_sock:close()
465                 client_sock = socket.try(socket.connect(host, port))
466             end
467
468         end
469     else
470         ssl = axtlsl.ssl_client_new(ssl_ctx, client_sock:getfd(), nil, 0)
471     end
472
473     -- check the return status
474     res = axtlsl.ssl_handshake_status(ssl)
475     if res ~= axtlsl.SSL_OK then
476         if not quiet then axtlsl.ssl_display_error(res) end
477         os.exit(1)
478     end
479
480     if not quiet then
481         local common_name = axtlsl.ssl_get_cert_dn(ssl, 
482                             axtlsl.SSL_X509_CERT_COMMON_NAME)
483
484         if common_name ~= nil then 
485             print("Common Name:\t\t\t"..common_name)
486         end
487
488         display_session_id(ssl)
489         display_cipher(ssl)
490     end
491
492     while true do
493         local line = io.read()
494     if line == nil then break end
495         local bytes = {}
496
497         for i = 1, #line do
498             bytes[i] = line.byte(line, i)
499         end
500
501         bytes[#line+1] = 10      -- add carriage return, null
502         bytes[#line+2] = 0
503
504         res = axtlsl.ssl_write(ssl, bytes, #bytes)
505         if res < axtlsl.SSL_OK then
506             if not quiet then axtlsl.ssl_display_error(res) end
507             break
508         end
509     end
510
511     axtlsl.ssl_ctx_free(ssl_ctx)
512     client_sock:close()
513 end
514
515 --
516 -- Display what cipher we are using 
517 --
518 function display_cipher(ssl)
519     io.write("CIPHER is ")
520     local cipher_id = axtlsl.ssl_get_cipher_id(ssl)
521
522     if cipher_id == axtlsl.SSL_AES128_SHA then
523         print("AES128-SHA")
524     elseif cipher_id == axtlsl.SSL_AES256_SHA then
525         print("AES256-SHA")
526     elseif axtlsl.SSL_RC4_128_SHA then
527         print("RC4-SHA")
528     elseif axtlsl.SSL_RC4_128_MD5 then
529         print("RC4-MD5")
530     else 
531         print("Unknown - "..cipher_id)
532     end
533 end
534
535 --
536 -- Display what session id we have.
537 --
538 function display_session_id(ssl)
539     local session_id = axtlsl.ssl_get_session_id(ssl)
540     local v
541
542     if #session_id > 0 then
543         print("-----BEGIN SSL SESSION PARAMETERS-----")
544         for _, v in ipairs(session_id) do
545             io.write(string.format("%02x", v))
546         end
547         print("\n-----END SSL SESSION PARAMETERS-----")
548     end
549 end
550
551 --
552 -- Main entry point. Doesn't do much except works out whether we are a client
553 -- or a server.
554 --
555 if #arg == 0 or (arg[1] ~= "s_server" and arg[1] ~= "s_client") then
556     print_options(#arg > 0 and arg[1] or "")
557 end
558
559 local build_mode = axtlsl.ssl_get_config(axtlsl.SSL_BUILD_MODE)
560 _ = arg[1] == "s_server" and do_server(build_mode) or do_client(build_mode)
561 os.exit(0)
562