Merge pull request #278 from nmav/ocserv
[project/luci.git] / libs / 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