1
0
Fork 0
mirror of https://github.com/puma/puma.git synced 2022-11-09 13:48:40 -05:00

ssl: Add Client Side Certificate Auth

Add Client Side Certificate Auth feature and handling to puma's MiniSSL. Also exposes SSL errors to puma/apps.

 compatibility notes: MRI only

 shell example:

   puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&ca=path_to_ca&verify_mode=force_peer'

 code example: (examples/client_side_ssl)

    app = proc {|env| p env['puma.peercert']; [200, {}, ["hey"]] }

    events = SSLEvents.new($stdout, $stderr)
    server = Puma::Server.new(app, events)

    admin_context             = Puma::MiniSSL::Context.new
    admin_context.key         = KEY_PATH
    admin_context.cert        = CERT_PATH
    admin_context.ca          = CA_CERT_PATH
    admin_context.verify_mode = Puma::MiniSSL::VERIFY_PEER | Puma::MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT

    server.add_ssl_listener("0.0.0.0", ADMIN_PORT, admin_context)
    server.min_threads = MIN_THREADS
    server.max_threads = MAX_THREADS
    server.persistent_timeout = IDLE_TIMEOUT
    server.run.join

 additional credits: Andy Alness <andy.alness@gmail.com>
This commit is contained in:
Julian Langschaedel 2015-01-14 05:11:26 +01:00
parent 6479e6b26b
commit e8d25b30f3
21 changed files with 675 additions and 14 deletions

View file

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDEDCCAfigAwIBAgIBATANBgkqhkiG9w0BAQUFADA4MRMwEQYKCZImiZPyLGQB
GRYDbmV0MRQwEgYKCZImiZPyLGQBGRYEcHVtYTELMAkGA1UEAwwCY2EwIBcNMTQw
MjAyMjAwODI3WhgPMjExNTAxMDkyMDA4MjdaMDgxEzARBgoJkiaJk/IsZAEZFgNu
ZXQxFDASBgoJkiaJk/IsZAEZFgRwdW1hMQswCQYDVQQDDAJjYTCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAN0u8/heGbkoFDDsx6uidE6DKPvjIPnZPFzR
CMkzrNgIeq/hfAItIJAO0m8YZivkUWeE3ut4ibSL+OVTvLRWDL/L736LILUxrD2f
joKHHLSVIUWl3H0VjYDE2RCiVkvxP4sAo7EYecZesTtb7W7DdAjHztFZIl+wT+ri
MlxDRmYxwsOPQtL0/wJZF80uTpC29V47NY9ITd/A+1xMblPAuQKO3vqZ4Yq07mO/
KKSbepo07v7jMhNOSHf8VBFlTzzG5AHmxZUW0qjCkJBV8N1MiT9cIk81ZuSqOZu3
A+aDAlOYPJe2WVpGskCme9HkJaHTeP87tQUsLqRsLgq/AXh5R58CAwEAAaMjMCEw
DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQAD
ggEBAKiGZ57rSAlL9slQ0FklVjpe+YrfZvmaXTRPl+9YhikoVI91u1r/qA9PrXKn
cL6u66SU6kJwI5572uT1TpKO7jQLXJZV0LO17WuF3P7y44QnNb53Em2GYi8DD/gq
X0Y1u8QzIxo4uomWiE73fnao2I9eErKNi/xCySaX/SLQ/9tcEgUyeLlTtJZ3feVF
7K0llR+hSb0Wy/uWnP7qP59YsyCJl1H23j7IEVCTMsOQ4tyIK16+qRA+aVLtE9f5
orsrOWWGJOdAn1nCJweKqhG1vd3GKGRW3Rf/iugCbvgJy0NFLfTpeJ4fJosC3A/K
6K+pe9hNsi2kBPwC67QeVjnbqd4=
-----END CERTIFICATE-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA3S7z+F4ZuSgUMOzHq6J0ToMo++Mg+dk8XNEIyTOs2Ah6r+F8
Ai0gkA7SbxhmK+RRZ4Te63iJtIv45VO8tFYMv8vvfosgtTGsPZ+OgocctJUhRaXc
fRWNgMTZEKJWS/E/iwCjsRh5xl6xO1vtbsN0CMfO0VkiX7BP6uIyXENGZjHCw49C
0vT/AlkXzS5OkLb1Xjs1j0hN38D7XExuU8C5Ao7e+pnhirTuY78opJt6mjTu/uMy
E05Id/xUEWVPPMbkAebFlRbSqMKQkFXw3UyJP1wiTzVm5Ko5m7cD5oMCU5g8l7ZZ
WkayQKZ70eQlodN4/zu1BSwupGwuCr8BeHlHnwIDAQABAoIBABjXyj1OTHNYhhQM
tEyZ3Zhn8PWByFVnyfje3a7DqBlHsogIuoYADZVApPAnfGpXpbEL4oHuMwFda2JO
qnZS5/Gu9UJwXAceAiuVvUr55AaAbZFGFOLTxeX9tifBJBI5kZqKQtiEWEEop510
MNHtEB5gWuF2sn6u7fsC1wc34zNdE/4hR1njsppIlJ1GP0ICQUI/YKybipF6+pI7
+vg7bF5DU93/uUoRY83NjREeAswFE67OOHi592YIyr5TLu06NDqJ2EBneXyO63Q7
pAakX84SI7k8p8t9XBW6D56pT23JYcrDXwayH1P+SAA4FdDhPlZlGPWjXqQc0biA
l1HlfiECgYEA8bwVsukWSGE+XdRnilVATP4zTS7ZpcYzUufS9+pa5w0sVIkDScgL
3YrB0rY7BS/kImOg+xrFz7ILCoIyjDCEVtly0Hc1aZw9SWu545lfFWcd7s7nN5nQ
iM9jGxoAWu/VT2GKIfhxMK89CzLD1DGgqsiyYxmPMyplekupUEkkiHECgYEA6jxl
uNddzzfKZKHEWS1Tax0hgchOaVMyML35ySz5kgw1tSO8G64eKtrSCq7wcSvb4uc3
hz1yl5Ydxqa+1qX5Qi+UcoRhZZHqGTsbid1aiQJKltk4ImtlptBbX+NTvEDIDblQ
fzse/a+upesutwaTchlXtuPG2F53UZeQ831GWQ8CgYAqKdtDDILVdxiwtwakS0Be
7Yu3L6/IyWxUTpkuotLeMB8GU6ueJ+Vh6/zoqt5ahkLteKEwizfrhSuF1rXIXAIJ
P/5VvCU12YmbD84pk6vRCN5gs/gCa7LC2iF4La3YLrLvGJ1GVZYwnrAwDte3YDyc
7UqoHGIs031FuoK6vTdBEQKBgHG5bz3mOqKgGMDxFY6ihgzMcPc9FGzousaVhhAZ
qPYyvWS7+9mImRb/dNlBBHY98B1jWz9rIxbcCIrpbGB05ucuiKltAoi45mrnmsA9
23YHycUho7J6aDkskiClE4OkBD09iwqq3qoWwPnHjL/KDo5oJYEjZ+inPNE9gF/n
o98bAoGBAMLZ7BYOXU1svCwuEz9RdAyXsrOX+Z9DW9i6WMVlfk9K1IxwpXYCvOjO
J+wJuQtuNbwKqNPw1DUEp/25cDVoekRAxaKgYGlJFib8vEGtbQ7GQK9bDA11/sIz
PQSfc92Y4+qpQ9WzhsZXip49itzBFgmN7/4eaohpvyHCFkVCZVpf
-----END RSA PRIVATE KEY-----

View file

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDAzCCAeugAwIBAgIBAzANBgkqhkiG9w0BAQUFADA4MRMwEQYKCZImiZPyLGQB
GRYDbmV0MRQwEgYKCZImiZPyLGQBGRYEcHVtYTELMAkGA1UEAwwCY2EwIBcNMTQw
MjAyMjAwODI3WhgPMjExNTAxMDkyMDA4MjdaMDwxEzARBgoJkiaJk/IsZAEZFgNu
ZXQxFDASBgoJkiaJk/IsZAEZFgRwdW1hMQ8wDQYDVQQDDAZjbGllbnQwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGnFKNrNj9pvHIK+iUf1UoDJ0O7hpj
jn7QWd65xnHWN9YF9RfBVRxg7HLcPls6GL4c+e5KQP1W0o4gSzwbUc3a/LyqkTEA
dligEjXTkQY6tCn/51CuClynreQ98wdgQrayzobKhWMALG7IRLraprZmiQJpxWOF
evd7WkF32AwSklZMEdWcLdI36swTV0UzuR9IDUnIh5GGPbikF/6hQ1E1+rL/sZkh
czYNEniJGk2pD3MqJguTvYTF24k1KEOV5koSuAPnyl4E/dX9g3AIHWo8OhqVs/4P
hrX6++qmrsVz9LIvPw3+SMAE3QU49J7uAANRQMBlxWhlbIpeFi8zNig7AgMBAAGj
EjAQMA4GA1UdDwEB/wQEAwIEsDANBgkqhkiG9w0BAQUFAAOCAQEAtxbX3CfQgMwa
CWYTjupyTC+KDajbkLLsNXw49PeTIj0FOjAdKA1zyEZcrxtaU+flJr8QHdI8HyZH
hpofnOTSBg5k9y4Qz8gjI1Nsh0H8WU/d7F//2l2fUDOhVAb6JtTAKnMpU4snb0GD
bxcO6QxfNh50Qdb7KoJH7baJ3aAnsRrLVGqQ7jH20iMu163j/pYw4dDskFMr65Le
bMB3NeQ5pHwtYf2J5EliKCtH+Df/BTIl9u1vviZs84gA0Odai/YaMZWCqFiWqIax
lkMHNSDWh2G++qMn9erLjRtYDAbIt3VhMncUpEBx3lBEIaVg7qyfpWQ4EkkkylH6
WRv06vukVg==
-----END CERTIFICATE-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxpxSjazY/abxyCvolH9VKAydDu4aY45+0FneucZx1jfWBfUX
wVUcYOxy3D5bOhi+HPnuSkD9VtKOIEs8G1HN2vy8qpExAHZYoBI105EGOrQp/+dQ
rgpcp63kPfMHYEK2ss6GyoVjACxuyES62qa2ZokCacVjhXr3e1pBd9gMEpJWTBHV
nC3SN+rME1dFM7kfSA1JyIeRhj24pBf+oUNRNfqy/7GZIXM2DRJ4iRpNqQ9zKiYL
k72ExduJNShDleZKErgD58peBP3V/YNwCB1qPDoalbP+D4a1+vvqpq7Fc/SyLz8N
/kjABN0FOPSe7gADUUDAZcVoZWyKXhYvMzYoOwIDAQABAoIBAA7C+aPMEAiyStAs
60l2OVcTsOy2J8H0ilpkA5jdNgLM/ZxNvilBcS2HBXZ3MAKeairvLJXaRLoaRjQC
Q4JoTxuSo1cuGW1GXonvMI77/XGJiIGbqLR20rIny4oLMSYnbzrU/NG6nkQaCVXb
PeQYdgAi+MnxwNbf79r8N1d3+FW85vjczo2aobWnJpir8U+xp5pe4xpqP8nddP7v
tdfIku8HBt76pu4ZfZynO6z9C+ZKS1s7YmBkGNuE46kwl9dhdmZGawAHxNYNAAJS
FPLHR11f3syjtPUUm3MAr5BlCFd6vgWYJFrdKv8/uH++WAiVvApnG/FjPh90i42p
muGHJbECgYEA+eiHPNzlCwYpPzY+n/AfBQ48G7sYeHk/yEDvhkJOnwAQEQMMw1s9
AGHFTaKa2rb6fruZp8qCXiuzhWq2x7e5+W2Buj+VU15fI1JCJW1s2GdlGOAclvEW
HvhcmlwtmSOWgTv1bbVSgQZB9hjbVK+81yQ9hn6AxJUT9k1IU3HALGkCgYEAy3Ow
DBX97AVn+1h4I/7cTqzjjLCaBn4UVcy0s3hsbvW/b+aYb8a3F4wJ7mWXvFk9Lb4h
uMfka6DYHszma1Bp7BEr63QIAhf936PXAljgtBBCron+9Y6DD83mz/9sX/MTo/pE
2J/qHOqYwoboDoscgtVXZxNM3UX70RJ6RBKAKwMCgYEAxpX+kWC/KWl18WM7lICN
Rckv/qFIKsO+6XSgYcHjE/pKyhnwVHT2Ho2S6cRi5ZYtq/OLgIgt3INBnq1UHZRj
1k8snUHVeXAujbTaFz/DFJvk/EVqso9Vkrqta4QAQAbFnGB3APzrWNgOJm9OKxeT
KisEMRHpZU1JlZmH9bcYjLECgYAPb1tvz0tQWKim3PNgZ7l3Do7E4bENxQrt53Xe
F8jCMkqvxqLR+BVz59/pAjQcyfhmPAJ67k9aCv3aeFkS0yr2Ced3GXpyDjfoe5mY
R/3kK0ejzjxVjNZMoKZeKVajgOGAk0Ad3yP3xaSJPYrlb5BeLKlQ3Jn8P473MZut
BmpK2QKBgQCr7aaFL5Ypv21kBKVI478jk7v/6PcYOztkFbOsje5A4SlkhlaE5u0h
iK0jON8MnAieLeP5QvyXy5n6wL/6THUSxpm3ZJRXpNgKHqENJrZBh3HpmrtzXjxF
WLMGl20yrsNUE8WR8wAJ5ECxwUPGZazDY9CD6C0Vm0LUWYdgZQnjnA==
-----END RSA PRIVATE KEY-----

View file

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDCTCCAfGgAwIBAgIBBDANBgkqhkiG9w0BAQUFADA4MRMwEQYKCZImiZPyLGQB
GRYDbmV0MRQwEgYKCZImiZPyLGQBGRYEcHVtYTELMAkGA1UEAwwCY2EwHhcNMTQw
MjAyMjAwODI3WhcNMTQwODA0MDgwODI3WjBEMRMwEQYKCZImiZPyLGQBGRYDbmV0
MRQwEgYKCZImiZPyLGQBGRYEcHVtYTEXMBUGA1UEAwwOY2xpZW50LWV4cGlyZWQw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZQSVfI4KNwus/+9gE9Rww
+cehxzw80fNi4tmruSApitTIk1u1r1rYVexkBkVTtl6Fg/aNAAdsI4aATanyGj0m
yRqEMxYMt8RtzAYHY6ZEJBm4WUAa44W7WNG2ZA/e0bCDq4Sn+hlPJw0e4iQimJqi
8+iitgyTdicTKDR+9kTS3W/33PZqSwqqnN55m9n9A5FIKwd8fbPsO8k6xIhFS2sL
KZ2TkAYLNXu2vFGJR7b37U8mYcHObB1p7U7WYJ2JCf21WZOC4iI25Xk7MFSUYPqb
W/iV+41EcslbHwAZHEjqeNynKNlnZokVrviOFeFrHqXbVKp43027L3RZr/JXfxMl
AgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIEsDANBgkqhkiG9w0BAQUFAAOCAQEAdCLR
jmHeQDrtl9w0cr8Vls+clhoWSDIEj2NC7PRUbDS5T0kAnF/N64n9RJFPS+4bpZaT
c9v3DXzdaTTp7moUrwVc3EKVLV5EJcm+TcuUhbL2ZnRgFHggVaoePShBHkDJGLz9
lR30KJnKsyFKEDEyD4rYtYvg98858EtkuxKLsD8efQ/9V8WDLAJJWTsJweEbEpIq
GqblQnBeNrLZ7yS32NAM9jnB9wPsMXPZnAAV/o/U6TTwIO9ChApWX+qer1/mIoc7
90/XhxEVw6EcXfGPnsLJ85n9FNGbWnLFRxvFAYcD0z6KQYxVHDiUAMSKqAkpENYO
k3gVOw5YNxNpPmUrjw==
-----END CERTIFICATE-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA2UElXyOCjcLrP/vYBPUcMPnHocc8PNHzYuLZq7kgKYrUyJNb
ta9a2FXsZAZFU7ZehYP2jQAHbCOGgE2p8ho9JskahDMWDLfEbcwGB2OmRCQZuFlA
GuOFu1jRtmQP3tGwg6uEp/oZTycNHuIkIpiaovPoorYMk3YnEyg0fvZE0t1v99z2
aksKqpzeeZvZ/QORSCsHfH2z7DvJOsSIRUtrCymdk5AGCzV7trxRiUe29+1PJmHB
zmwdae1O1mCdiQn9tVmTguIiNuV5OzBUlGD6m1v4lfuNRHLJWx8AGRxI6njcpyjZ
Z2aJFa74jhXhax6l21SqeN9Nuy90Wa/yV38TJQIDAQABAoIBAQDOhvSc7aflVZ/H
koT3qX8kO78AVuM3uiqSHa7pZTJi63x+ND9hhxJoR75SE/gBrYNLj3ho79cegOMS
w0HEShdJ8LFJbTsP2f5cljBBBAUCEAN3UTj0lsgBolyx84t2uYYAlaOk/8bhjPEX
I8lQLhwKvq2vSDrKT+6zcmv9KeWhQWoQavq+QAkTVO9gAqmlkdMEjZntT8hau/qC
jkgW7MG/U/CkbALcrbhAWBtMSvUDSKHrrva7XPaMq5nDvX0Wj6PZhY9KaaweR8ZR
xfrgzbFfRSKdbT5dD7IcwQhjV51hev6+q8pIFgTiFimeNq4TvKgH5MMwixBnVM+3
djBTB4+dAoGBAO5BYdbpEuVDlwMfHo//R/BJEGJn9dwc3ZpBmJ6vQGmLGjf/oXBr
9tDf/yZKDLwVAgnRdVkllMxpEWrFjD3OpnukbvzTijBi0AQAljRSwNlMTsLwAifi
EBXvENFG/7iJKssCQBD6rkeNir3VRlMa5khI9jHahZ0B53RtQCYYDzZ/AoGBAOlv
W005wD3g9K2P5BIo+qXB43ZFFsAFOnhu7jUyTciu/95iJ+zw/AGHI/JhNy+jSHnw
ZCARLy1c2CImAshadYuWDqR5okR+xHHj3Lgf9ig7lbSf+skn3R4y6fTlxxNdbbU7
dbZbiMm5CyUHTR6957BQaS7mfQZJG0OP5G9fl0xbAoGAOSNa+HRbALqN68S5yqTZ
Nsn+8OqnrssJZiYXGO9Ejks61XUr3U83GO6vPRqDJVQQchRWhTObFM6Zy7ZmpKf7
iylrKJz+xg3cfyk43IGAGFzRgrSWf8QaQXhc2yOgzjuvFJKMlMXZp/VM8avFOsb3
tRwyVtBmPLopLOXKfZhFhbcCgYA/etbbU18h9LDVGhItlhNDTEys9vDO2x0hbxk8
QifA8UYHla3B027Ug4mU+jblr4OgFW1FAydPMLZd4vRSw7a/dNkahTFJayfEyPBW
6eoo2rtFWVP7q+mHstTIkkvmyjtxU3AZXR7/rGCJe0jPmVkOK2/PH0LUmMDfSJwY
ZWhhjQKBgDCB823bmF6+7J0mtNFFKvRMz6k0wKz7Qe6+AkwmyR3v9IBpL4UMFgIq
xdRR7iGhlRHaWVZyzG3WQ1ZgLmVUsfmk9OrD5PfhKaElKvaRr8e+MHOesQ6AgWW2
YXr6vgr6tykVtjG4/v98r05+9q10HH0xOhbuBz+1P7IyLfTCWxbE
-----END RSA PRIVATE KEY-----

View file

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDEzCCAfugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBAMRMwEQYKCZImiZPyLGQB
GRYDbmV0MRQwEgYKCZImiZPyLGQBGRYEcHVtYTETMBEGA1UEAwwKY2EtdW5rbm93
bjAgFw0xNDAyMDIyMDA4MjdaGA8yMTE1MDEwOTIwMDgyN1owRDETMBEGCgmSJomT
8ixkARkWA25ldDEUMBIGCgmSJomT8ixkARkWBHB1bWExFzAVBgNVBAMMDmNsaWVu
dC11bmtub3duMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Ewf7nWJ
WcHI3NB+gjz5jWnpNwnU47hjIWq+S41iIymN7FsNdssfzeGb3ZElcQAfofvNf75F
6mE7YEpbKRU7t/Nptvx+Rd1YGOS2N/PWdj3IcJgvQ2guiU0aYkdB7lC1vlI0QzHT
dte9pGK/ZPp/mvRZGwi9WmwlNhBwOzvdyRuLyi63dmZ8vgyZrfbmGhZYdhCZ77Uv
i+VYqYv5X30I2gQkV6YQMj/AF5Fmt9a4TNGfIjXb6FKmNhlsDMduovrfQMh2umMK
YYQ4A+Vi2yMAZkKeD5cogGLS8wmg76n7miPaKLVU9xTEf55IO+HjIBIqz6VG8qbg
iBV4Lr6BkkaKTQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCBLAwDQYJKoZIhvcNAQEF
BQADggEBAGGaA7fZLOqxx1wIIay0Ewni3ljzR+RAlpTHAh4x+NilcaQ2ils+JoGH
/DCdX2iD5nevGVm1DANBhfAuFxXGGBjoOLqtg/sO7Rk51IV9WjDVB2rGeH3hoTCk
Qi6Bazdlcvvs3SyFEKcJm2zXizR7O9I+tDv++F6bbaHSBWB6tB9g93pZuMR+smvR
Ll2+/jRGPe1Pif1UFs5DR8QshpvxrIwCmO1vznLhDeA5Pde6CtahGJvi1Y25L1h0
9l0LjMxxqgVh8h4A5AR8VufCcDiaT8lzCkz4G4jQYFhrJXmBn8Em6NZfdP/LmM9I
0zEB2Y3lp32ng+WMyaqNh6nfpxEfBoY=
-----END CERTIFICATE-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA0Ewf7nWJWcHI3NB+gjz5jWnpNwnU47hjIWq+S41iIymN7FsN
dssfzeGb3ZElcQAfofvNf75F6mE7YEpbKRU7t/Nptvx+Rd1YGOS2N/PWdj3IcJgv
Q2guiU0aYkdB7lC1vlI0QzHTdte9pGK/ZPp/mvRZGwi9WmwlNhBwOzvdyRuLyi63
dmZ8vgyZrfbmGhZYdhCZ77Uvi+VYqYv5X30I2gQkV6YQMj/AF5Fmt9a4TNGfIjXb
6FKmNhlsDMduovrfQMh2umMKYYQ4A+Vi2yMAZkKeD5cogGLS8wmg76n7miPaKLVU
9xTEf55IO+HjIBIqz6VG8qbgiBV4Lr6BkkaKTQIDAQABAoIBAC41pSPWqWjjJ7ds
/ZPRCR/JLjbKlJMMVdmU/7BtJidc0aJstLj06RJYiaaGy8Kc32elH/rF8GbFuVFs
TXr4ve3aL0qsCytepmunWZFiI+LJZA0uhdWzaBeHpmHFIyhGeXtGa1e41wvXYrf0
PDefpu1uZdIshy1nLn4m+W76ogI6FrcyUQ+XRN6f5x6af70Kfi414qf9NLOvajnl
JCx90WpdwBp1jC8totKDp9kvILCymFnGBl93NUl8F3Tz7zLkh2SIhaKxQIHTc9g0
LPNkd1Q5tIl/rG3lZ6vw2/UlCVB1NdLsItlJVIJhu6ChIrSXeoDg0GNlJFp7op6W
jlJDoRUCgYEA67Nuzrv/lkVljRFDVw97DTiCbLL7SDXDH6Jf43+Bz6prGLqd4Nfd
LTO7AdLGd5UTWB1Oj2U94pFXNFkucGv65ck6QAPZ4UrCk9lyjx/dN7d6CxUBDeu2
4zPeHy/mAgkVmKSzEy5L2ERwoQqK8A/g6HtuThB87gtKUpnG+QSfz6sCgYEA4jyE
kPRudqne0VXWL/Mhnrls2PNo4MDIOxUp+KcFLGY/44RIEoit3WLRDXULAgT3U20Z
lY7nQJyU+/CaEH6rIpADp6VRPA0XJo7HCuMGEO7bk8AAXkXTplVs+XbsD0221AKl
GRpS0CtcvHllHiwG8iL+zHAJzL4wbNY36L97decCgYB0UuHk9bN2Hlm3/UUWunUo
WTNFIjARuzbJbgGU7WDLdHfWhINWbDKkFFu+0p9QdSpO2mfjLTwVjVVUaI8avK/e
qCkvXrcxEQxmm3KGYFt1HAAHaB5VGHfyOa7uBV2ms4UNCHu4g6i620warnFTeQKu
ufv+WvTNJpVPnsUsMLQOcQKBgQDcfgDxydjTPDIWseLjrsGIkc29EFaaHinIM5NJ
bXbEVA9WbflUXvOc/g8jX3xQBokKPR2fPryxoyos9c0h4GJoeBWn0Z5/uX5jrOne
+W5TGIjW0l1JhCKITV+9LqNZMvPKY52G/rnRe0GRy3q60kwet+6/Tz6t1nsZyBqL
c/we5wKBgQDc0rE459diZTpxKzumgUGlutKWhqPDGO+NwMa8xaaPvn/k6bg5avx2
8by3BSWhc/YEK7qtVcO1sDr7m9dHtqxrk8+2CC+ZI6wfc359xB9uImrbs9Jqz3VZ
+Ji2VOirgm/oZNzhpi2l7yG2atXg0PqLMkS/ft6gWyAjzy/Q8WDSjQ==
-----END RSA PRIVATE KEY-----

View file

@ -0,0 +1,78 @@
require "bundler/setup"
require "puma"
require "puma/minissl"
case ARGV[0]
when "s"
app = proc {|env|
p env['puma.peercert']
[200, {}, [ env['puma.peercert'] ]]
}
events = Puma::Events.new($stdout, $stderr)
server = Puma::Server.new(app, events)
context = Puma::MiniSSL::Context.new
context.key = "certs/server.key"
context.cert = "certs/server.crt"
context.ca = "certs/ca.crt"
#context.verify_mode = Puma::MiniSSL::VERIFY_NONE
#context.verify_mode = Puma::MiniSSL::VERIFY_PEER
context.verify_mode = Puma::MiniSSL::VERIFY_PEER | Puma::MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
server.add_ssl_listener("127.0.0.1", 4000, context)
server.run
sleep
#server.stop(true)
when "g"
def issue_cert(dn, key, serial, not_before, not_after, extensions, issuer, issuer_key, digest)
cert = OpenSSL::X509::Certificate.new
issuer = cert unless issuer
issuer_key = key unless issuer_key
cert.version = 2
cert.serial = serial
cert.subject = dn
cert.issuer = issuer.subject
cert.public_key = key.public_key
cert.not_before = not_before
cert.not_after = not_after
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = issuer
extensions.each{|oid, value, critical|
cert.add_extension(ef.create_extension(oid, value, critical))
}
cert.sign(issuer_key, digest)
cert
end
@ca_key = OpenSSL::PKey::RSA.generate(2048)
@svr_key = OpenSSL::PKey::RSA.generate(2048)
@cli_key = OpenSSL::PKey::RSA.generate(2048)
@ca = OpenSSL::X509::Name.parse("/DC=net/DC=client-cbhq/CN=CA")
@svr = OpenSSL::X509::Name.parse("/DC=net/DC=client-cbhq/CN=localhost")
@cli = OpenSSL::X509::Name.parse("/DC=net/DC=client-cbhq/CN=localhost")
now = Time.at(Time.now.to_i)
ca_exts = [
["basicConstraints","CA:TRUE",true],
["keyUsage","cRLSign,keyCertSign",true],
]
ee_exts = [
#["keyUsage","keyEncipherment,digitalSignature",true],
["keyUsage","keyEncipherment,dataEncipherment,digitalSignature",true],
]
@ca_cert = issue_cert(@ca, @ca_key, 1, now, now+3600_000, ca_exts, nil, nil, OpenSSL::Digest::SHA1.new)
@svr_cert = issue_cert(@svr, @svr_key, 2, now, now+1800_000, ee_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
@cli_cert = issue_cert(@cli, @cli_key, 3, now, now+1800_000, ee_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
File.open("ca.crt","wb"){|f| f.print @ca_cert.to_pem }
File.open("ca.key","wb"){|f| f.print @ca_key.to_pem }
File.open("server.crt","wb"){|f| f.print @svr_cert.to_pem }
File.open("server.key","wb"){|f| f.print @svr_key.to_pem }
File.open("client1.crt","wb"){|f| f.print @cli_cert.to_pem }
File.open("client1.key","wb"){|f| f.print @cli_key.to_pem }
end

View file

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDBjCCAe6gAwIBAgIBAjANBgkqhkiG9w0BAQUFADA4MRMwEQYKCZImiZPyLGQB
GRYDbmV0MRQwEgYKCZImiZPyLGQBGRYEcHVtYTELMAkGA1UEAwwCY2EwIBcNMTQw
MjAyMjAwODI3WhgPMjExNTAxMDkyMDA4MjdaMD8xEzARBgoJkiaJk/IsZAEZFgNu
ZXQxFDASBgoJkiaJk/IsZAEZFgRwdW1hMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDK2ioejidqPJzhGgYh9Nc/CD8g
n/vFqchULvmG796R01Rx0Xk5v7OgWs4GMhvJ8o4soCxTmACyPStdemDlocdzZf2d
yfv1alVVfBBwqSsiekiB7IiSvpyg5t3h8XqWJcKtP00tPEYmAkVuMbVSxQPrsEi5
47kxu7zyiV0RavaZbODgxkupSjEr0DHa1h7pkip53ekz/rnoceVcvSnCdOahUVj6
ZwMkOQtay/b6746ttbfQh1ygbqTbV/lcV9erldlDkqKG0gQ6gxaBcbIiom1p+ohu
CcoTDGZu431KOU6ZygbGxaIEZY9Zbyg9Dp+o6Zyyd7UTY/0JcCWUq7O/XaN5AgMB
AAGjEjAQMA4GA1UdDwEB/wQEAwIEsDANBgkqhkiG9w0BAQUFAAOCAQEAIfMqanJJ
aVD6XuS3aj0I31L4RiPSfKhkPiuO+lqBGzZhUEKwnEqVWLosFF1SK8Inbu1c1uyP
zRb0tB4nSO01L8Oc5kTfuN9lr3nNaWDpGksa/S5e9WndQk95XF3FLt7FJii8wWnM
9xGW27lurskbpuZc1M7IkD5W90y2fF19qB8fY8B2RGovPJEsDKSZ7pwSozijGR4Q
2iIY4Lk9/vYxEYMRixE2+exYiKTNfaPt+CgxHxXksn0LvbYYQTxUmDgvSxXdrnCc
4Kb1BbxOmB8XF17aJuRdUJxDxlnQK5LpoUWGfW7jFPbfX4d3nzpxjPaxvr3peRQV
DNtRoD9mFvocbQ==
-----END CERTIFICATE-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAytoqHo4najyc4RoGIfTXPwg/IJ/7xanIVC75hu/ekdNUcdF5
Ob+zoFrOBjIbyfKOLKAsU5gAsj0rXXpg5aHHc2X9ncn79WpVVXwQcKkrInpIgeyI
kr6coObd4fF6liXCrT9NLTxGJgJFbjG1UsUD67BIueO5Mbu88oldEWr2mWzg4MZL
qUoxK9Ax2tYe6ZIqed3pM/656HHlXL0pwnTmoVFY+mcDJDkLWsv2+u+OrbW30Idc
oG6k21f5XFfXq5XZQ5KihtIEOoMWgXGyIqJtafqIbgnKEwxmbuN9SjlOmcoGxsWi
BGWPWW8oPQ6fqOmcsne1E2P9CXAllKuzv12jeQIDAQABAoIBABBL4JBd2TrGrc/D
uHRn6BbvQasMTzy8/BQPRgqaIKZUdPdD3dpO1U5vnReQVP0vWE6re4QntP6cvWwg
FcK88XoK2oofnPdFWJ+qfOOgI4/8hPCzIPGxEII4qeCp9rAzTmV+rWOR8QzCp/NH
WQrSOxNnMSCF8+3T6EUP1gM9NZxzpycvoa3Xk4QduPdSN9+ifLOohvRwq0PsP8z5
6tPIxEyHTmUjJhDh0en2fXFs6ncxvzQJ+p8R3cSaACDDmAR3uuKgpinC7zLKRyIB
rqThVMOO+yxMYNNZ+JIJbuaAAAQy1znPfPsy8syFEvOBhZXJNLEpTqr7n4kHvxpW
MI8ukTUCgYEA6zCSMEb9lH/z/qgJPYbZcUuT/M9/EJdiDlazfYrzAHsmwL/FRXjc
vAJCeayy3A/oMBWJ+tQrRCPb0e/LU2kqLJRWENKN7uTAHGntDJOtM94ZWDLcAySv
zo6usr7BhLmP8ySVojjbWoWI4+SHONYcxsk1v5O7f0ZbzMoDoQPPcl8CgYEA3M0Y
l8mDcPlm90r0/CKq5egpzWvb6dvz5Sly83bJIK1CnjyZUbmQZSO2fp9fFFffZ3SG
tbgDJ5xQ5Ie+H2mTCsCqkIRqi8tCnbHCXcN40N3SXxcS4e4UcMhVCAHrGODqHrAb
if8uTxwozxZtYklaZwhszdtY0lWRG2BzILfOKScCgYBOjyvVqnDboJ3cyz5C6f9J
48fr41d7MEXVqkpMPhSLbZd1PNllKkj5F/wibnhUH5AcN6WePi6xlRTBHEsbcn5e
47GX7uzwBkLReuRulgl90MtAdcSd3CxJX8mk9Sjo757QxcChrkI/C2m9TcGJT6PP
Fri4ZF111wek8TmjGAW8GwKBgDhuuvBgcpW3SJe/sqmWerNUCQsVnBlDPCy/0T9k
hrcxUSt8NXtrv/n5jLUEKpracqDQaXWcWEIRc6NVBkSlCQ3gfDd/gHPGOXpwakro
oMJRT2k6TnssDFFfAkyPoPS012GMhR1Z+Q4DFnMHOmG6eb6HqrdabnMjp3ilyAb+
s1RVAoGAQCGfhL3j9ShiTlpbOcL6CdERk4Jzw7mD4g6gVvyKLJWwACl5Y7YgVcfU
Bsm9c3GM2OkAAHDlYd8oBvaWArI5eN93zLgD4uU/Bm08SKpQqOOghqrFQy3B9Ngr
eEgVYYvmHikJfcUzOYfotRdH4APGt8EAL2007oyox7Yucv5pzNA=
-----END RSA PRIVATE KEY-----

View file

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDIDCCAgigAwIBAgIBATANBgkqhkiG9w0BAQUFADBAMRMwEQYKCZImiZPyLGQB
GRYDbmV0MRQwEgYKCZImiZPyLGQBGRYEcHVtYTETMBEGA1UEAwwKY2EtdW5rbm93
bjAgFw0xNDAyMDIyMDA4MjdaGA8yMTE1MDEwOTIwMDgyN1owQDETMBEGCgmSJomT
8ixkARkWA25ldDEUMBIGCgmSJomT8ixkARkWBHB1bWExEzARBgNVBAMMCmNhLXVu
a25vd24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDlvCM8F2q9vE7d
PMgao53q0U3MIJgNMOw4eZGDfxZrbGM3x5Zws3/7jzngE8BFc/Y+RhC73T/dN6E9
ZT2jfwlRRSUxx8Pq2OUMA9Pb8fj8TAoLGwC6txKaqy/UqKqhVGjQ3FUS3cXBzR85
PGN9mhIB72+ftcWzw0KSNb+pYG8tg+1p6Nb+UlSrjS9/Z0KM8zKnteMG75qhtKnC
rtD6RBiqp98c5r/JJ+LANODaCjtVj5SJTVd/MyshvrNlfYPlMgt+/tU8qSlKzwMa
HcN7KA+oT0blOojaUNJMjgqwCI8QeTP1/DEDfvJvTtzPkaz/ctrmbHzQvLS8Lh6f
KVv32cg9AgMBAAGjIzAhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG
MA0GCSqGSIb3DQEBBQUAA4IBAQCpp/LR62GvtZVrscMVKMfHtlotU67g1nP+FESE
7nJ5Av4rhaxRFHkF1YdQINyB6mL4fHzDu1g4aLdZmTRjOZcYw6Y2xrJZ/X1lIg29
7X4s5AlyHJUstWJnk/FrycPBJqZ75b5SJOayaMiAW+fEsQM2wETISkLitQyVlU3V
CtITVjcvgrnsFmnN/qi75EnxxkohZFZGtC2f/NZufYmbpB2FHMt9hhddG7nMawGK
dnpbEAiDiQO757Td3vSfAQN6ahopwe2YbrgirrwMQpScoy5pKdbrhMXTLCuwXZmj
KR6n2WyS0IzminNy1M4FeB4Pq82VH4rFPwl+t6PWjSHaF87V
-----END CERTIFICATE-----

View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA5bwjPBdqvbxO3TzIGqOd6tFNzCCYDTDsOHmRg38Wa2xjN8eW
cLN/+4854BPARXP2PkYQu90/3TehPWU9o38JUUUlMcfD6tjlDAPT2/H4/EwKCxsA
urcSmqsv1KiqoVRo0NxVEt3Fwc0fOTxjfZoSAe9vn7XFs8NCkjW/qWBvLYPtaejW
/lJUq40vf2dCjPMyp7XjBu+aobSpwq7Q+kQYqqffHOa/ySfiwDTg2go7VY+UiU1X
fzMrIb6zZX2D5TILfv7VPKkpSs8DGh3DeygPqE9G5TqI2lDSTI4KsAiPEHkz9fwx
A37yb07cz5Gs/3La5mx80Ly0vC4enylb99nIPQIDAQABAoIBAQCbxT6K30HkFsvO
nQj9bxWDg5nhn/QZdaOmA2AULlbwTdTUnIM4Na3Az3OpqRrEvQUpYm60Qyergq3U
qFHsCxYxQdYfc9k24wwjYnEDgIWX5KMmto9/CuUVdJ+A7UCNFWPgwpT4ruEJMGFM
eNLo9k/heg1Q2HqOEgaQhttHKHkZ/UJaR6XXeucBfJtXSIWf42omeDRNhlwsQ+LY
WbTv3XmiFbu1Bhkk67xlpyuGEL1g9Auz9P2z+2Q2LV66kNhfInIFtUYFeNpkjgUJ
TtDRU7UBOm+YPMjDfjVUzPbzvCVAtxG4t0ZSJJcQF3N4+HfpoL1c33CCqYwkh1KI
xJi1CgjtAoGBAPR3u8OovMmSJ6tdee0WyTahYmK6VOtmSm4IJf1t/wUvf/u6X/Q6
U06TxUAiAs8rMwvvtgPeLYxtaEKO0PSD0rHNL1MHnBwYAmLvAFyCc5tuuyb3ZIyg
1oAz/hW5bYgAL32nmDrlwq0W+KU478SRWWYZA2raO3Ha08I1YVbgkSHPAoGBAPCS
fIexjEPxeyZJe4+iKQcmWW42HA9rWIpu/9FDZxvn+PpWhBwMzlxjTlQS2V3nMSHp
Jtzyj+Y2R1SO8OQoqZ6s7G+cv8Ni+FqOidcUPUH6aDc2A0ihb0FANp4FQsrv7riP
W12mWfniTxZri+nKAwpXjEjko8yi05go3y0dTjQzAoGAJq+n6/+Q2Ikjc+/X8pfv
gZCqZBs+gv3t+1mYwXEdsTFiHHDS7HAqbL3fshVvwl8AtfvaHuSS6q0JmbbGBFu0
BOUGfyouHxgBkKxnrzwJlWhBf5oYtFRjfWg85i0w0xvMaCMUaQWg+AkxkdvfvYiO
0CRXMRqV25+YcRxHahshfGsCgYEAus9Vql1B6YS8N4f6ThgDOg0ahw23jnWyJJV7
SznG+JGS8np6TfnXyUBIE9srNdMQgR+20P3+piriCxSQlOvKg3AOjcEv2/6fklp7
SSvrQa+8e5sSw7SwWwANKXo2WrYkLucLcNZ7qiKFfYh39kyrPb2sLvJ1C7QpEVAz
tam7D6cCgYBdqh+SlwryiX351eh+tLOlysAPJ1cb3JotnZY1WYKfR9r5PlJsisut
dcjOOaz5/Uo/UlVKOjOxxUuB8FIIJGPvx6lo0hq8ornS0CdqhDspUx4aAD0iZ6+y
iccYnG0CLW3HpS4B1B7a8ktXW59m+tT9Fl+usOmwdPNIzcdAMqff+A==
-----END RSA PRIVATE KEY-----

View file

@ -5,6 +5,7 @@
#include <openssl/ssl.h>
#include <openssl/dh.h>
#include <openssl/err.h>
#include <openssl/x509.h>
typedef struct {
BIO* read;
@ -13,7 +14,17 @@ typedef struct {
SSL_CTX* ctx;
} ms_conn;
typedef struct {
unsigned char* buf;
int bytes;
} ms_cert_buf;
void engine_free(ms_conn* conn) {
ms_cert_buf* cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
if(cert_buf) {
OPENSSL_free(cert_buf->buf);
free(cert_buf);
}
SSL_free(conn->ssl);
SSL_CTX_free(conn->ctx);
@ -73,6 +84,32 @@ DH *get_dh1024() {
return dh;
}
static int engine_verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
X509* err_cert;
SSL* ssl;
int bytes;
unsigned char* buf = NULL;
if(!preverify_ok) {
err_cert = X509_STORE_CTX_get_current_cert(ctx);
if(err_cert) {
/*
* Save the failed certificate for inspection/logging.
*/
bytes = i2d_X509(err_cert, &buf);
if(bytes > 0) {
ms_cert_buf* cert_buf = (ms_cert_buf*)malloc(sizeof(ms_cert_buf));
cert_buf->buf = buf;
cert_buf->bytes = bytes;
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
SSL_set_app_data(ssl, cert_buf);
}
}
}
return preverify_ok;
}
VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
VALUE obj;
SSL_CTX* ctx;
@ -86,11 +123,21 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
ID sym_cert = rb_intern("cert");
VALUE cert = rb_funcall(mini_ssl_ctx, sym_cert, 0);
ID sym_ca = rb_intern("ca");
VALUE ca = rb_funcall(mini_ssl_ctx, sym_ca, 0);
ID sym_verify_mode = rb_intern("verify_mode");
VALUE verify_mode = rb_funcall(mini_ssl_ctx, sym_verify_mode, 0);
ctx = SSL_CTX_new(SSLv23_server_method());
conn->ctx = ctx;
SSL_CTX_use_certificate_file(ctx, RSTRING_PTR(cert), SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM);
if (!NIL_P(ca)) {
SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
}
SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE);
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
@ -106,10 +153,15 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
EC_KEY_free(ecdh);
}
ssl = SSL_new(ctx);
ssl = SSL_new(ctx);
conn->ssl = ssl;
SSL_set_app_data(ssl, NULL);
/* SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); */
if (NIL_P(verify_mode)) {
/* SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); */
} else {
SSL_set_verify(ssl, NUM2INT(verify_mode), engine_verify_callback);
}
SSL_set_bio(ssl, conn->read, conn->write);
@ -123,6 +175,7 @@ VALUE engine_init_client(VALUE klass) {
conn->ctx = SSL_CTX_new(DTLSv1_method());
conn->ssl = SSL_new(conn->ctx);
SSL_set_app_data(conn->ssl, NULL);
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
SSL_set_bio(conn->ssl, conn->read, conn->write);
@ -151,11 +204,36 @@ VALUE engine_inject(VALUE self, VALUE str) {
static VALUE eError;
void raise_error(SSL* ssl, int result) {
int error = SSL_get_error(ssl, result);
char* msg = ERR_error_string(error, NULL);
char buf[512];
char msg[512];
const char* err_str;
int err = errno;
int ssl_err = SSL_get_error(ssl, result);
int verify_err = SSL_get_verify_result(ssl);
if(SSL_ERROR_SYSCALL == ssl_err) {
strerror_r(err, buf, sizeof(buf));
snprintf(msg, sizeof(msg), "System error: %s - %d", buf, err);
} else if(SSL_ERROR_SSL == ssl_err) {
if(X509_V_OK != verify_err) {
err_str = X509_verify_cert_error_string(verify_err);
snprintf(msg, sizeof(msg),
"OpenSSL certificate verification error: %s - %d",
err_str, verify_err);
} else {
err = ERR_get_error();
ERR_error_string_n(err, buf, sizeof(buf));
snprintf(msg, sizeof(msg), "OpenSSL error: %s - %d", buf, err);
}
} else {
snprintf(msg, sizeof(msg), "Unknown OpenSSL error: %d", ssl_err);
}
ERR_clear_error();
rb_raise(eError, "OpenSSL error: %s - %d", msg, error);
rb_raise(eError, msg);
}
VALUE engine_read(VALUE self) {
@ -165,6 +243,8 @@ VALUE engine_read(VALUE self) {
Data_Get_Struct(self, ms_conn, conn);
ERR_clear_error();
bytes = SSL_read(conn->ssl, (void*)buf, sizeof(buf));
if(bytes > 0) {
@ -174,24 +254,26 @@ VALUE engine_read(VALUE self) {
if(SSL_want_read(conn->ssl)) return Qnil;
error = SSL_get_error(conn->ssl, bytes);
if(error == SSL_ERROR_ZERO_RETURN || error == SSL_ERROR_SSL) {
rb_eof_error();
}
raise_error(conn->ssl, bytes);
if(error == SSL_ERROR_ZERO_RETURN) {
rb_eof_error();
} else {
raise_error(conn->ssl, bytes);
}
return Qnil;
}
VALUE engine_write(VALUE self, VALUE str) {
ms_conn* conn;
char buf[512];
int bytes;
Data_Get_Struct(self, ms_conn, conn);
StringValue(str);
ERR_clear_error();
bytes = SSL_write(conn->ssl, (void*)RSTRING_PTR(str), (int)RSTRING_LEN(str));
if(bytes > 0) {
return INT2FIX(bytes);
@ -225,6 +307,45 @@ VALUE engine_extract(VALUE self) {
return Qnil;
}
VALUE engine_peercert(VALUE self) {
ms_conn* conn;
X509* cert;
int bytes;
unsigned char* buf = NULL;
ms_cert_buf* cert_buf = NULL;
VALUE rb_cert_buf;
Data_Get_Struct(self, ms_conn, conn);
cert = SSL_get_peer_certificate(conn->ssl);
if(!cert) {
/*
* See if there was a failed certificate associated with this client.
*/
cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
if(!cert_buf) {
return Qnil;
}
buf = cert_buf->buf;
bytes = cert_buf->bytes;
} else {
bytes = i2d_X509(cert, &buf);
X509_free(cert);
if(bytes < 0) {
return Qnil;
}
}
rb_cert_buf = rb_str_new(buf, bytes);
if(!cert_buf) {
OPENSSL_free(buf);
}
return rb_cert_buf;
}
void Init_mini_ssl(VALUE puma) {
VALUE mod, eng;
@ -246,4 +367,6 @@ void Init_mini_ssl(VALUE puma) {
rb_define_method(eng, "write", engine_write, 1);
rb_define_method(eng, "extract", engine_extract, 0);
rb_define_method(eng, "peercert", engine_peercert, 0);
}

View file

@ -155,9 +155,28 @@ module Puma
end
ctx.cert = params['cert']
end
ctx.verify_mode = MiniSSL::VERIFY_NONE
if ['peer', 'force_peer'].include?(params['verify_mode'])
unless params['ca']
@events.error "Please specify the SSL ca via 'ca='"
end
ctx.ca = params['ca']
end
if params['verify_mode']
ctx.verify_mode = case params['verify_mode']
when "peer"
MiniSSL::VERIFY_PEER
when "force_peer"
MiniSSL::VERIFY_PEER | MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
when "none"
MiniSSL::VERIFY_NONE
else
@events.error "Please specify a valid verify_mode="
MiniSSL::VERIFY_NONE
end
end
end
if fd = @inherited_fds.delete(str)
logger.log "* Inherited #{str}"

View file

@ -129,6 +129,7 @@ module Puma
RACK_AFTER_REPLY = "rack.after_reply".freeze
PUMA_SOCKET = "puma.socket".freeze
PUMA_CONFIG = "puma.config".freeze
PUMA_PEERCERT = "puma.peercert".freeze
HTTP = "http".freeze
HTTPS = "https".freeze

View file

@ -96,6 +96,15 @@ module Puma
@stderr.puts "#{Time.now}: ENV: #{env.inspect}\n---\n"
end
# An SSL error has occured.
# +server+ is the Server object, +peeraddr+ peer address, +peercert+
# any peer certificate (if present), and +error+ an exception object.
#
def ssl_error(server, peeraddr, peercert, error)
subject = peercert ? peercert.subject : nil
@stderr.puts "#{Time.now}: SSL error, peer: #{peeraddr}, peer cert: #{subject}, #{error.inspect}"
end
# An unknown error has occured.
# +server+ is the Server object, +env+ the request, +error+ an exception
# object, and +kind+ some additional info.

View file

@ -4,6 +4,7 @@ module Puma
def initialize(socket, engine)
@socket = socket
@engine = engine
@peercert = nil
end
def to_io
@ -86,6 +87,21 @@ module Puma
def peeraddr
@socket.peeraddr
end
def peercert
return @peercert if @peercert
raw = @engine.peercert
return nil unless raw
@peercert = OpenSSL::X509::Certificate.new raw
end
end
if defined?(JRUBY_VERSION)
class SSLError < StandardError
# Define this for jruby even though it isn't used.
end
end
class Context
@ -104,6 +120,7 @@ module Puma
# non-jruby Context properties
attr_reader :key
attr_reader :cert
attr_reader :ca
def key=(key)
raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
@ -114,11 +131,17 @@ module Puma
raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
@cert = cert
end
def ca=(ca)
raise ArgumentError, "No such ca file '#{ca}'" unless File.exist? ca
@ca = ca
end
end
end
VERIFY_NONE = 0
VERIFY_PEER = 1
VERIFY_FAIL_IF_NO_PEER_CERT = 2
class Server
def initialize(socket, ctx)

View file

@ -74,6 +74,17 @@ module Puma
sockets.delete c
end
# SSL handshake failure
rescue MiniSSL::SSLError => e
ssl_socket = c.io
addr = ssl_socket.peeraddr.last
cert = ssl_socket.peercert
c.close
sockets.delete c
@events.ssl_error @server, addr, cert, e
# The client doesn't know HTTP well
rescue HttpParserError => e
c.write_400

View file

@ -250,6 +250,14 @@ module Puma
client.finish
process_now = true
end
rescue MiniSSL::SSLError => e
ssl_socket = client.io
addr = ssl_socket.peeraddr.last
cert = ssl_socket.peercert
client.close
@events.ssl_error self, addr, cert, e
rescue HttpParserError => e
client.write_400
client.close
@ -395,6 +403,16 @@ module Puma
rescue ConnectionError
# Swallow them. The ensure tries to close +client+ down
# SSL handshake error
rescue MiniSSL::SSLError => e
ssl_socket = client.io
addr = ssl_socket.peeraddr.last
cert = ssl_socket.peercert
close_socket = true
@events.ssl_error self, addr, cert, e
# The client doesn't know HTTP well
rescue HttpParserError => e
client.write_400
@ -467,6 +485,7 @@ module Puma
end
def default_server_port(env)
return PORT_443 if env[HTTPS_KEY] == 'on' || env[HTTPS_KEY] == 'https'
env['HTTP_X_FORWARDED_PROTO'] == 'https' ? PORT_443 : PORT_80
end
@ -487,6 +506,10 @@ module Puma
env[PUMA_SOCKET] = client
if env[HTTPS_KEY] && client.peercert
env[PUMA_PEERCERT] = client.peercert
end
env[HIJACK_P] = true
env[HIJACK] = req

View file

@ -8,6 +8,16 @@ require 'puma/server'
require 'net/https'
class SSLEventsHelper < ::Puma::Events
attr_accessor :addr, :cert, :error
def ssl_error(server, peeraddr, peercert, error)
self.addr = peeraddr
self.cert = peercert
self.error = error
end
end
class TestPumaServerSSL < Test::Unit::TestCase
def setup
@ -28,7 +38,7 @@ class TestPumaServerSSL < Test::Unit::TestCase
@ctx.verify_mode = Puma::MiniSSL::VERIFY_NONE
@events = Puma::Events.new STDOUT, STDERR
@events = SSLEventsHelper.new STDOUT, STDERR
@server = Puma::Server.new @app, @events
@server.add_ssl_listener @host, @port, @ctx
@server.run
@ -95,6 +105,94 @@ class TestPumaServerSSL < Test::Unit::TestCase
Net::HTTP::Get.new '/'
end
end
unless defined?(JRUBY_VERSION)
assert_match("wrong version number", @events.error.message) if @events.error
end
end
end
end
# client-side TLS authentication tests
unless defined?(JRUBY_VERSION)
class TestPumaServerSSLClient < Test::Unit::TestCase
def assert_ssl_client_error_match(error, subject=nil, &blk)
@port = 3212
@host = "127.0.0.1"
@app = lambda { |env| [200, {}, [env['rack.url_scheme']]] }
@ctx = Puma::MiniSSL::Context.new
@ctx.key = File.expand_path "../../examples/puma/client-certs/server.key", __FILE__
@ctx.cert = File.expand_path "../../examples/puma/client-certs/server.crt", __FILE__
@ctx.ca = File.expand_path "../../examples/puma/client-certs/ca.crt", __FILE__
@ctx.verify_mode = Puma::MiniSSL::VERIFY_PEER | Puma::MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
events = SSLEventsHelper.new STDOUT, STDERR
@server = Puma::Server.new @app, events
@server.add_ssl_listener @host, @port, @ctx
@server.run
@http = Net::HTTP.new @host, @port
@http.use_ssl = true
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
blk.call(@http)
client_error = false
begin
@http.start do
req = Net::HTTP::Get.new "/", {}
@http.request(req)
end
rescue OpenSSL::SSL::SSLError
client_error = true
end
sleep 0.1
assert_equal !!error, client_error
assert_match error, events.error.message if error
assert_equal @host, events.addr if error
assert_equal subject, events.cert.subject.to_s if subject
ensure
@server.stop(true)
end
def test_verify_fail_if_no_client_cert
assert_ssl_client_error_match 'peer did not return a certificate' do |http|
# nothing
end
end
def test_verify_fail_if_client_unknown_ca
assert_ssl_client_error_match('self signed certificate in certificate chain', '/DC=net/DC=puma/CN=ca-unknown') do |http|
key = File.expand_path "../../examples/puma/client-certs/client_unknown.key", __FILE__
crt = File.expand_path "../../examples/puma/client-certs/client_unknown.crt", __FILE__
http.key = OpenSSL::PKey::RSA.new File.read(key)
http.cert = OpenSSL::X509::Certificate.new File.read(crt)
http.ca_file = File.expand_path "../../examples/puma/client-certs/unknown_ca.crt", __FILE__
end
end
def test_verify_fail_if_client_expired_cert
assert_ssl_client_error_match('certificate has expired', '/DC=net/DC=puma/CN=client-expired') do |http|
key = File.expand_path "../../examples/puma/client-certs/client_expired.key", __FILE__
crt = File.expand_path "../../examples/puma/client-certs/client_expired.crt", __FILE__
http.key = OpenSSL::PKey::RSA.new File.read(key)
http.cert = OpenSSL::X509::Certificate.new File.read(crt)
http.ca_file = File.expand_path "../../examples/puma/client-certs/ca.crt", __FILE__
end
end
def test_verify_client_cert
assert_ssl_client_error_match(nil) do |http|
key = File.expand_path "../../examples/puma/client-certs/client.key", __FILE__
crt = File.expand_path "../../examples/puma/client-certs/client.crt", __FILE__
http.key = OpenSSL::PKey::RSA.new File.read(key)
http.cert = OpenSSL::X509::Certificate.new File.read(crt)
http.ca_file = File.expand_path "../../examples/puma/client-certs/ca.crt", __FILE__
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
end
end
end