From 41bfc7a7218d34b9b9ffbe8154a728b2e044211f Mon Sep 17 00:00:00 2001 From: drbrain Date: Mon, 6 Dec 2010 00:54:44 +0000 Subject: Add toplevel documentation for OpenSSL Add additional documentation for OpenSSL::SSL::SSLContext and OpenSSL::SSL::SSLSocket. Move "let rdoc know about mOSSL" comments so they don't show up in output. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@30090 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/openssl/ossl.c | 308 ++++++++++++++++++++++++++++++++++++++- ext/openssl/ossl_asn1.c | 4 +- ext/openssl/ossl_bn.c | 4 +- ext/openssl/ossl_cipher.c | 4 +- ext/openssl/ossl_digest.c | 4 +- ext/openssl/ossl_hmac.c | 4 +- ext/openssl/ossl_pkey.c | 4 +- ext/openssl/ossl_pkey_dh.c | 4 +- ext/openssl/ossl_pkey_dsa.c | 4 +- ext/openssl/ossl_pkey_rsa.c | 4 +- ext/openssl/ossl_rand.c | 4 +- ext/openssl/ossl_ssl.c | 319 ++++++++++++++++++++++++++++++++++++----- ext/openssl/ossl_ssl_session.c | 4 +- 13 files changed, 615 insertions(+), 56 deletions(-) (limited to 'ext/openssl') diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index b3f76c26fc..aa6f9f9200 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -407,7 +407,313 @@ ossl_debug_set(VALUE self, VALUE val) } /* - * OSSL library init + * OpenSSL provides SSL, TLS and general purpose cryptography. It wraps the + * OpenSSL[http://www.openssl.org/] library. + * + * = Examples + * + * All examples assume you have loaded OpenSSL with: + * + * require 'openssl' + * + * These examples build atop each other. For example the key created in the + * next is used in throughout these examples. + * + * == Keys + * + * === Creating a Key + * + * This example creates a 2048 bit RSA keypair and writes it to the current + * directory. + * + * key = OpenSSL::PKey::RSA.new 2048 + * + * open 'private_key.pem', 'w' do |io| io.write key.to_pem end + * open 'public_key.pem', 'w' do |io| io.write key.public_key.to_pem end + * + * === Exporting a Key + * + * Keys saved to disk without encryption are not secure as anyone who gets + * ahold of the key may use it unless it is encrypted. In order to securely + * export a key you may export it with a pass phrase. + * + * cipher = OpenSSL::Cipher::Cipher.new 'AES-128-CBC' + * pass_phrase = 'my secure pass phrase goes here' + * + * key_secure = key.export cipher, pass_phrase + * + * open 'private.secure.pem', 'w' do |io| + * io.write key_secure + * end + * + * OpenSSL::Cipher.ciphers returns a list of available ciphers. + * + * === Loading a Key + * + * A key can also be loaded from a file. + * + * key2 = OpenSSL::PKey::RSA.new File.read 'private_key.pem' + * key2.public? # => true + * + * or + * + * key3 = OpenSSL::PKey::RSA.new File.read 'public_key.pem' + * key3.private? # => false + * + * === Loading an Encrypted Key + * + * OpenSSL will prompt you for your pass phrase when loading an encrypted key. + * If you will not be able to type in the pass phrase you may provide it when + * loading the key: + * + * key4_pem = File.read 'private.secure.pem' + * key4 = OpenSSL::PKey::RSA.new key4_pem, pass_phrase + * + * == X509 Certificates + * + * === Creating a Certificate + * + * This example creates a self-signed certificate using an RSA key and a SHA1 + * signature. + * + * name = OpenSSL::X509::Name.parse 'CN=nobody/DC=example' + * + * cert = OpenSSL::X509::Certificate.new + * cert.version = 2 + * cert.serial = 0 + * cert.not_before = Time.now + * cert.not_after = Time.now + 3600 + * + * cert.public_key = key.public_key + * cert.subject = name + * + * === Certificate Extensions + * + * You can add extensions to the certificate with + * OpenSSL::SSL::ExtensionFactory to indicate the purpose of the certificate. + * + * extension_factory = OpenSSL::X509::ExtensionFactory.new nil, cert + * + * extension_factory.create_extension 'basicConstraints', 'CA:FALSE' + * extension_factory.create_extension 'keyUsage', + * 'keyEncipherment,dataEncipherment,digitalSignature' + * extension_factory.create_extension 'subjectKeyIdentifier', 'hash' + * + * === Signing a Certificate + * + * To sign a certificate set the issuer and use OpenSSL::X509::Certificate#sign + * with a digest algorithm. This creates a self-signed cert because we're using + * the same name and key to sign the certificate as was used to create the + * certificate. + * + * cert.issuer = name + * cert.sign key, OpenSSL::Digest::SHA1.new + * + * open 'certificate.pem', 'w' do |io| io.write cert.to_pem end + * + * === Loading a Certificate + * + * Like a key, a cert can also be loaded from a file. + * + * cert2 = OpenSSL::X509::Certificate.new File.read 'certificate.pem' + * + * === Verifying a Certificate + * + * Certificate#verify will return true when a certificate was signed with the + * given public key. + * + * raise 'certificate can not be verified' unless cert2.verify key + * + * == Certificate Authority + * + * A certificate authority (CA) is a trusted third party that allows you to + * verify the ownership of unknown certificates. The CA issues key signatures + * that indicate it trusts the user of that key. A user encountering the key + * can verify the signature by using the CA's public key. + * + * === CA Key + * + * CA keys are valuable, so we encrypt and save it to disk and make sure it is + * not readable by other users. + * + * ca_key = OpenSSL::PKey::RSA.new 2048 + * + * cipher = OpenSSL::Cipher::AES.new 128, :CBC + * + * open 'ca_key.pem', 'w', 0400 do |io| + * io.write key.export(cipher, pass_phrase) + * end + * + * === CA Certificate + * + * A CA certificate is created the same way we created a certificate above, but + * with different extensions. + * + * ca_name = OpenSSL::X509::Name.parse 'CN=ca/DC=example' + * + * ca_cert = OpenSSL::X509::Certificate.new + * ca_cert.serial = 0 + * ca_cert.version = 2 + * ca_cert.not_before = Time.now + * ca_cert.not_after = Time.now + 86400 + * + * ca_cert.public_key = ca_key.public_key + * ca_cert.subject = ca_name + * ca_cert.issuer = ca_name + * + * extension_factory = OpenSSL::X509::ExtensionFactory.new + * extension_factory.subject_certificate = ca_cert + * extension_factory.issuer_certificate = ca_cert + * + * extension_factory.create_extension 'subjectKeyIdentifier', 'hash' + * + * This extension indicates the CA's key may be used as a CA. + * + * extension_factory.create_extension 'basicConstraints', 'CA:TRUE', true + * + * This extension indicates the CA's key may be used to verify signatures on + * both certificates and certificate revocations. + * + * extension_factory.create_extension 'keyUsage', 'cRLSign,keyCertSign', true + * + * Root CA certificates are self-signed. + * + * ca_cert.sign ca_key, OpenSSL::Digest::SHA1.new + * + * The CA certificate is saved to disk so it may be distributed to all the + * users of the keys this CA will sign. + * + * open 'ca_cert.pem', 'w' do |io| + * io.write ca_cert.to_pem + * end + * + * === Certificate Signing Request + * + * The CA signs keys through a Certificate Signing Request (CSR). The CSR + * contains the information necessary to identify the key. + * + * csr = OpenSSL::X509::Request.new + * csr.version = 0 + * csr.subject = name + * csr.public_key = key.public_key + * csr.sign key, OpenSSL::Digest::SHA1.new + * + * A CSR is saved to disk and sent to the CA for signing. + * + * open 'csr.pem', 'w' do |io| + * io.write csr.to_pem + * end + * + * === Creating a Certificate from a CSR + * + * Upon receiving a CSR the CA will verify it before signing it. A minimal + * verification would be to check the CSR's signature. + * + * csr = OpenSSL::X509::Request.new File.read 'csr.pem' + * + * raise 'CSR can not be verified' unless csr.verify csr.public_key + * + * After verification a certificate is created, marked for various usages, + * signed with the CA key and returned to the requester. + * + * csr_cert = OpenSSL::X509::Certificate.new + * csr_cert.serial = 0 + * csr_cert.version = 2 + * csr_cert.not_before = Time.now + * csr_cert.not_after = Time.now + 600 + * + * csr_cert.subject = csr.subject + * csr_cert.public_key = csr.public_key + * csr_cert.issuer = ca_cert.subject + * + * extension_factory = OpenSSL::X509::ExtensionFactory.new + * extension_factory.subject_certificate = csr_cert + * extension_factory.issuer_certificate = ca_cert + * + * extension_factory.create_extension 'basicConstraints', 'CA:FALSE' + * extension_factory.create_extension 'keyUsage', + * 'keyEncipherment,dataEncipherment,digitalSignature' + * extension_factory.create_extension 'subjectKeyIdentifier', 'hash' + * + * csr_cert.sign ca_key, OpenSSL::Digest::SHA1.new + * + * open 'csr_cert.pem', 'w' do |io| + * io.write csr_cert.to_pem + * end + * + * == SSL and TLS Connections + * + * Using our created key and certificate we can create an SSL or TLS connection. + * An SSLContext is used to set up an SSL session. + * + * context = OpenSSL::SSL::SSLContext.new + * + * === SSL Server + * + * An SSL server requires the certificate and private key to communicate + * securely with its clients: + * + * context.cert = cert + * context.key = key + * + * Then create an SSLServer with a TCP server socket and the context. Use the + * SSLServer like an ordinary TCP server. + * + * require 'socket' + * + * tcp_server = TCPServer.new 5000 + * ssl_server = OpenSSL::SSL::SSLServer.new tcp_server, context + * + * loop do + * ssl_connection = ssl_server.accept + * + * data = connection.gets + * + * response = "I got #{data.dump}" + * puts response + * + * connection.puts "I got #{data.dump}" + * connection.close + * end + * + * === SSL client + * + * An SSL client is created with a TCP socket and the context. + * SSLSocket#connect must be called to initiate the SSL handshake and start + * encryption. A key and certificate are not required for the client socket. + * + * require 'socket' + * + * tcp_client = TCPSocket.new 'localhost', 5000 + * ssl_client = OpenSSL::SSL::SSLSocket.new client_socket, context + * ssl_client.connect + * + * ssl_client.puts "hello server!" + * puts ssl_client.gets + * + * === Peer Verification + * + * An unverified SSL connection does not provide much security. For enhanced + * security the client or server can verify the certificate the of its peer. + * + * The client can be modified to verify the server's certificate against the + * certificate authority's certificate: + * + * context.ca_file = 'ca_cert.pem' + * context.verify_mode = OpenSSL::SSL::VERIFY_PEER + * + * require 'socket' + * + * tcp_client = TCPSocket.new 'localhost', 5000 + * ssl_client = OpenSSL::SSL::SSLSocket.new client_socket, context + * ssl_client.connect + * + * ssl_client.puts "hello server!" + * puts ssl_client.gets + * + * If the server certificate is invalid or context.ca_file is not set + * when verifying peers an OpenSSL::SSL::SSLError will be raised. + * */ void Init_openssl() diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index a9ab7e38ce..add316462f 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -1087,8 +1087,8 @@ Init_ossl_asn1() VALUE ary; int i; -#if 0 /* let rdoc know about mOSSL */ - mOSSL = rb_define_module("OpenSSL"); +#if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ #endif sUNIVERSAL = rb_intern("UNIVERSAL"); diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index 6adc59fc1a..0a91697aa1 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -731,8 +731,8 @@ ossl_bn_is_prime_fasttest(int argc, VALUE *argv, VALUE self) void Init_ossl_bn() { -#if 0 /* let rdoc know about mOSSL */ - mOSSL = rb_define_module("OpenSSL"); +#if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ #endif if (!(ossl_bn_ctx = BN_CTX_new())) { diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index 41dc6b7e3e..8b24df1452 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -518,8 +518,8 @@ static VALUE ossl_cipher_block_size() { } void Init_ossl_cipher(void) { -#if 0 /* let rdoc know about mOSSL */ - mOSSL = rb_define_module("OpenSSL"); +#if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ #endif cCipher = rb_define_class_under(mOSSL, "Cipher", rb_cObject); eCipherError = rb_define_class_under(cCipher, "CipherError", eOSSLError); diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c index 04679d37f9..29d5371518 100644 --- a/ext/openssl/ossl_digest.c +++ b/ext/openssl/ossl_digest.c @@ -235,8 +235,8 @@ Init_ossl_digest() { rb_require("digest"); -#if 0 /* let rdoc know about mOSSL */ - mOSSL = rb_define_module("OpenSSL"); +#if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ #endif cDigest = rb_define_class_under(mOSSL, "Digest", rb_path2class("Digest::Class")); diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c index 4c6bf77fda..d87fc3ea72 100644 --- a/ext/openssl/ossl_hmac.c +++ b/ext/openssl/ossl_hmac.c @@ -236,8 +236,8 @@ ossl_hmac_s_hexdigest(VALUE klass, VALUE digest, VALUE key, VALUE data) void Init_ossl_hmac() { -#if 0 /* let rdoc know about mOSSL */ - mOSSL = rb_define_module("OpenSSL"); +#if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ #endif eHMACError = rb_define_class_under(mOSSL, "HMACError", eOSSLError); diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 16ceff4997..56abf4e597 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -211,8 +211,8 @@ ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data) void Init_ossl_pkey() { -#if 0 /* let rdoc know about mOSSL */ - mOSSL = rb_define_module("OpenSSL"); +#if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ #endif mPKey = rb_define_module_under(mOSSL, "PKey"); diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index 97535db368..5ea575eaf9 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -489,8 +489,8 @@ ossl_create_dh(unsigned char *p, size_t plen, unsigned char *g, size_t glen) void Init_ossl_dh() { -#if 0 /* let rdoc know about mOSSL and mPKey */ - mOSSL = rb_define_module("OpenSSL"); +#if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL and mPKey */ mPKey = rb_define_module_under(mOSSL, "PKey"); #endif diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c index a6e7f8dc43..e66f4ed009 100644 --- a/ext/openssl/ossl_pkey_dsa.c +++ b/ext/openssl/ossl_pkey_dsa.c @@ -445,8 +445,8 @@ OSSL_PKEY_BN(dsa, priv_key) void Init_ossl_dsa() { -#if 0 /* let rdoc know about mOSSL and mPKey */ - mOSSL = rb_define_module("OpenSSL"); +#if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL and mPKey */ mPKey = rb_define_module_under(mOSSL, "PKey"); #endif diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index bfaeb43fbe..83a1e6a4b2 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -536,8 +536,8 @@ OSSL_PKEY_BN(rsa, iqmp) void Init_ossl_rsa() { -#if 0 /* let rdoc know about mOSSL and mPKey */ - mOSSL = rb_define_module("OpenSSL"); +#if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL and mPKey */ mPKey = rb_define_module_under(mOSSL, "PKey"); #endif diff --git a/ext/openssl/ossl_rand.c b/ext/openssl/ossl_rand.c index 97a1b9fa95..748e4c937e 100644 --- a/ext/openssl/ossl_rand.c +++ b/ext/openssl/ossl_rand.c @@ -181,8 +181,8 @@ ossl_rand_status(VALUE self) void Init_ossl_rand() { -#if 0 /* let rdoc know about mOSSL */ - mOSSL = rb_define_module("OpenSSL"); +#if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ #endif mRandom = rb_define_module_under(mOSSL, "Random"); diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 7fe06c0937..99a4fe3e97 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -152,7 +152,7 @@ ossl_sslctx_s_alloc(VALUE klass) * ctx.ssl_version = :TLSv1 * ctx.ssl_version = "SSLv23_client" * - * You can get a list of valid methods with OpenSSL::SSL::SSLContext::METHODS + * You can get a list of valid versions with OpenSSL::SSL::SSLContext::METHODS */ static VALUE ossl_sslctx_set_ssl_version(VALUE self, VALUE ssl_method) @@ -527,7 +527,8 @@ ssl_servername_cb(SSL *ssl, int *ad, void *arg) * ctx.setup => nil # thereafter * * This method is called automatically when a new SSLSocket is created. - * Normally you do not need to call this method (unless you are writing an extension in C). + * Normally you do not need to call this method (unless you are writing an + * extension in C). */ static VALUE ossl_sslctx_setup(VALUE self) @@ -687,6 +688,8 @@ ossl_ssl_cipher_to_ary(SSL_CIPHER *cipher) /* * call-seq: * ctx.ciphers => [[name, version, bits, alg_bits], ...] + * + * The list of ciphers configured for this context. */ static VALUE ossl_sslctx_get_ciphers(VALUE self) @@ -769,6 +772,7 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v) * call-seq: * ctx.session_add(session) -> true | false * + * Adds +session+ to the session cache */ static VALUE ossl_sslctx_session_add(VALUE self, VALUE arg) @@ -786,6 +790,7 @@ ossl_sslctx_session_add(VALUE self, VALUE arg) * call-seq: * ctx.session_remove(session) -> true | false * + * Removes +session+ from the session cache */ static VALUE ossl_sslctx_session_remove(VALUE self, VALUE arg) @@ -801,8 +806,9 @@ ossl_sslctx_session_remove(VALUE self, VALUE arg) /* * call-seq: - * ctx.session_cache_mode -> integer + * ctx.session_cache_mode -> Integer * + * The current session cache mode. */ static VALUE ossl_sslctx_get_session_cache_mode(VALUE self) @@ -816,8 +822,11 @@ ossl_sslctx_get_session_cache_mode(VALUE self) /* * call-seq: - * ctx.session_cache_mode=(integer) -> integer + * ctx.session_cache_mode=(integer) -> Integer * + * Sets the SSL session cache mode. Bitwise-or together the desired + * SESSION_CACHE_* constants to set. See SSL_CTX_set_session_cache_mode(3) for + * details. */ static VALUE ossl_sslctx_set_session_cache_mode(VALUE self, VALUE arg) @@ -833,8 +842,10 @@ ossl_sslctx_set_session_cache_mode(VALUE self, VALUE arg) /* * call-seq: - * ctx.session_cache_size -> integer + * ctx.session_cache_size -> Integer * + * Returns the current session cache size. Zero is used to represent an + * unlimited cache size. */ static VALUE ossl_sslctx_get_session_cache_size(VALUE self) @@ -848,8 +859,10 @@ ossl_sslctx_get_session_cache_size(VALUE self) /* * call-seq: - * ctx.session_cache_size=(integer) -> integer + * ctx.session_cache_size=(integer) -> Integer * + * Sets the session cache size. Returns the previously valid session cache + * size. Zero is used to represent an unlimited session cache size. */ static VALUE ossl_sslctx_set_session_cache_size(VALUE self, VALUE arg) @@ -867,6 +880,23 @@ ossl_sslctx_set_session_cache_size(VALUE self, VALUE arg) * call-seq: * ctx.session_cache_stats -> Hash * + * Returns a Hash containing the following keys: + * + * :accept:: Number of started SSL/TLS handshakes in server mode + * :accept_good:: Number of established SSL/TLS sessions in server mode + * :accept_renegotiate:: Number of start renegotiations in server mode + * :cache_full:: Number of sessions that were removed due to cache overflow + * :cache_hits:: Number of successfully reused connections + * :cache_misses:: Number of sessions proposed by clients that were not found + * in the cache + * :cache_num:: Number of sessions in the internal session cache + * :cb_hits:: Number of sessions retrieved from the external cache in server + * mode + * :connect:: Number of started SSL/TLS handshakes in client mode + * :connect_good:: Number of established SSL/TLS sessions in client mode + * :connect_renegotiate:: Number of start renegotiations in client mode + * :timeouts:: Number of sessions proposed by clients that were found in the + * cache but had expired due to timeouts */ static VALUE ossl_sslctx_get_session_cache_stats(VALUE self) @@ -898,6 +928,7 @@ ossl_sslctx_get_session_cache_stats(VALUE self) * call-seq: * ctx.flush_sessions(time | nil) -> self * + * Removes sessions in the internal cache that have expired at +time+. */ static VALUE ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE self) @@ -953,9 +984,11 @@ ossl_ssl_s_alloc(VALUE klass) * SSLSocket.new(io) => aSSLSocket * SSLSocket.new(io, ctx) => aSSLSocket * - * === Parameters - * * +io+ is a real ruby IO object. Not an IO like object that responds to read/write. - * * +ctx+ is an OpenSSLSSL::SSLContext. + * Creates a new SSL socket from +io+ which must be a real ruby object (not an + * IO-like object that responds to read/write. + * + * If +ctx+ is provided the SSL Sockets initial params will be taken from + * the context. * * The OpenSSL::Buffering module provides additional IO methods. * @@ -1101,6 +1134,9 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, int nonblock) /* * call-seq: * ssl.connect => self + * + * Initiates an SSL/TLS handshake with a server. The handshake may be started + * after unencrypted data has been sent over the socket. */ static VALUE ossl_ssl_connect(VALUE self) @@ -1113,7 +1149,7 @@ ossl_ssl_connect(VALUE self) * call-seq: * ssl.connect_nonblock => self * - * initiate the TLS/SSL handshake as a client in non-blocking manner. + * Initiates the SSL/TLS handshake as a client in non-blocking manner. * * # emulates blocking connect * begin @@ -1137,6 +1173,9 @@ ossl_ssl_connect_nonblock(VALUE self) /* * call-seq: * ssl.accept => self + * + * Waits for a SSL/TLS client to initiate a handshake. The handshake may be + * started after unencrypted data has been sent over the socket. */ static VALUE ossl_ssl_accept(VALUE self) @@ -1149,7 +1188,7 @@ ossl_ssl_accept(VALUE self) * call-seq: * ssl.accept_nonblock => self * - * initiate the TLS/SSL handshake as a server in non-blocking manner. + * Initiates the SSL/TLS handshake as a server in non-blocking manner. * * # emulates blocking accept * begin @@ -1235,9 +1274,8 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) * ssl.sysread(length) => string * ssl.sysread(length, buffer) => buffer * - * === Parameters - * * +length+ is a positive integer. - * * +buffer+ is a string used to store the result. + * Reads +length+ bytes from the SSL connection. If a pre-allocated +buffer+ + * is provided the data will be written into it. */ static VALUE ossl_ssl_read(int argc, VALUE *argv, VALUE self) @@ -1250,9 +1288,11 @@ ossl_ssl_read(int argc, VALUE *argv, VALUE self) * ssl.sysread_nonblock(length) => string * ssl.sysread_nonblock(length, buffer) => buffer * - * === Parameters - * * +length+ is a positive integer. - * * +buffer+ is a string used to store the result. + * A non-blocking version of #sysread. Raises an SSLError if reading would + * block. + * + * Reads +length+ bytes from the SSL connection. If a pre-allocated +buffer+ + * is provided the data will be written into it. */ static VALUE ossl_ssl_read_nonblock(int argc, VALUE *argv, VALUE self) @@ -1304,7 +1344,9 @@ ossl_ssl_write_internal(VALUE self, VALUE str, int nonblock) /* * call-seq: - * ssl.syswrite(string) => integer + * ssl.syswrite(string) => Integer + * + * Writes +string+ to the SSL connection. */ static VALUE ossl_ssl_write(VALUE self, VALUE str) @@ -1314,7 +1356,10 @@ ossl_ssl_write(VALUE self, VALUE str) /* * call-seq: - * ssl.syswrite_nonblock(string) => integer + * ssl.syswrite_nonblock(string) => Integer + * + * Writes +string+ to the SSL connection in a non-blocking manner. Raises an + * SSLError if writing would block. */ static VALUE ossl_ssl_write_nonblock(VALUE self, VALUE str) @@ -1325,6 +1370,8 @@ ossl_ssl_write_nonblock(VALUE self, VALUE str) /* * call-seq: * ssl.sysclose => nil + * + * Shuts down the SSL connection and prepares it for another connection. */ static VALUE ossl_ssl_close(VALUE self) @@ -1342,6 +1389,8 @@ ossl_ssl_close(VALUE self) /* * call-seq: * ssl.cert => cert or nil + * + * The X509 certificate for this socket endpoint. */ static VALUE ossl_ssl_get_cert(VALUE self) @@ -1370,6 +1419,8 @@ ossl_ssl_get_cert(VALUE self) /* * call-seq: * ssl.peer_cert => cert or nil + * + * The X509 certificate for this socket's peer. */ static VALUE ossl_ssl_get_peer_cert(VALUE self) @@ -1399,6 +1450,8 @@ ossl_ssl_get_peer_cert(VALUE self) /* * call-seq: * ssl.peer_cert_chain => [cert, ...] or nil + * + * The X509 certificate chain for this socket's peer. */ static VALUE ossl_ssl_get_peer_cert_chain(VALUE self) @@ -1429,6 +1482,8 @@ ossl_ssl_get_peer_cert_chain(VALUE self) /* * call-seq: * ssl.cipher => [name, version, bits, alg_bits] + * + * The cipher being used for the current connection */ static VALUE ossl_ssl_get_cipher(VALUE self) @@ -1449,6 +1504,8 @@ ossl_ssl_get_cipher(VALUE self) /* * call-seq: * ssl.state => string + * + * A description of the current connection state. */ static VALUE ossl_ssl_get_state(VALUE self) @@ -1471,7 +1528,9 @@ ossl_ssl_get_state(VALUE self) /* * call-seq: - * ssl.pending => integer + * ssl.pending => Integer + * + * The number of bytes that are immediately available for reading */ static VALUE ossl_ssl_pending(VALUE self) @@ -1488,9 +1547,10 @@ ossl_ssl_pending(VALUE self) } /* - * call-seq: - * ssl.session_reused? -> true | false + * call-seq: + * ssl.session_reused? -> true | false * + * Returns true if a reused session was negotiated during the handshake. */ static VALUE ossl_ssl_session_reused(VALUE self) @@ -1511,9 +1571,10 @@ ossl_ssl_session_reused(VALUE self) } /* - * call-seq: - * ssl.session = session -> session + * call-seq: + * ssl.session = session -> session * + * Sets the Session to be used when the connection is established. */ static VALUE ossl_ssl_set_session(VALUE self, VALUE arg1) @@ -1538,6 +1599,15 @@ ossl_ssl_set_session(VALUE self, VALUE arg1) return arg1; } +/* + * call-seq: + * ssl.verify_result => Integer + * + * Returns the result of the peer certificates verification. See verify(1) + * for error values and descriptions. + * + * If no peer certificate was presented X509_V_OK is returned. + */ static VALUE ossl_ssl_get_verify_result(VALUE self) { @@ -1558,8 +1628,8 @@ Init_ossl_ssl() int i; VALUE ary; -#if 0 /* let rdoc know about mOSSL */ - mOSSL = rb_define_module("OpenSSL"); +#if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ #endif ID_callback_state = rb_intern("@callback_state"); @@ -1577,18 +1647,161 @@ Init_ossl_ssl() Init_ossl_ssl_session(); - /* class SSLContext + /* Document-class: OpenSSL::SSL::SSLContext * - * The following attributes are available but don't show up in rdoc. - * All attributes must be set before calling SSLSocket.new(io, ctx). + * An SSLContext is used to set various options regarding certificates, + * algorithms, verification, session caching, etc. The SSLContext is + * used to create an SSLSocket. + * + * All attributes must be set before creating an SSLSocket as the + * SSLContext will be frozen afterward. + * + * The following attributes are available but don't show up in rdoc: * * ssl_version, cert, key, client_ca, ca_file, ca_path, timeout, * * verify_mode, verify_depth client_cert_cb, tmp_dh_callback, * * session_id_context, session_add_cb, session_new_cb, session_remove_cb */ cSSLContext = rb_define_class_under(mSSL, "SSLContext", rb_cObject); rb_define_alloc_func(cSSLContext, ossl_sslctx_s_alloc); - for(i = 0; i < numberof(ossl_sslctx_attrs); i++) - rb_attr(cSSLContext, rb_intern(ossl_sslctx_attrs[i]), 1, 1, Qfalse); + + /* + * Context certificate + */ + rb_attr(cSSLContext, rb_intern("cert"), 1, 1, Qfalse); + + /* + * Context private key + */ + rb_attr(cSSLContext, rb_intern("key"), 1, 1, Qfalse); + + /* + * A certificate or Array of certificates that will be sent to the client. + */ + rb_attr(cSSLContext, rb_intern("client_ca"), 1, 1, Qfalse); + + /* + * The path to a file containing a PEM-format CA certificate + */ + rb_attr(cSSLContext, rb_intern("ca_file"), 1, 1, Qfalse); + + /* + * The path to a directory containing CA certificates in PEM format. + * + * Files are looked up by subject's X509 name's hash value. + */ + rb_attr(cSSLContext, rb_intern("ca_path"), 1, 1, Qfalse); + + /* + * Maximum session lifetime. + */ + rb_attr(cSSLContext, rb_intern("timeout"), 1, 1, Qfalse); + + /* + * Session verification mode. + * + * Valid modes are VERIFY_NONE, VERIFY_PEER, VERIFY_CLIENT_ONCE, + * VERIFY_FAIL_IF_NO_PEER_CERT and defined on OpenSSL::SSL + */ + rb_attr(cSSLContext, rb_intern("verify_mode"), 1, 1, Qfalse); + + /* + * Number of CA certificates to walk when verifying a certificate chain. + */ + rb_attr(cSSLContext, rb_intern("verify_depth"), 1, 1, Qfalse); + + /* + * A callback for additional certificate verification. The callback is + * invoked for each certificate in the chain. + * + * The callback is invoked with two values. +preverify_ok+ indicates + * indicates if the verification was passed (true) or not (false). + * +store_context+ is an OpenSSL::X509::StoreContext containing the + * context used for certificate verification. + * + * If the callback returns false verification is stopped. + */ + rb_attr(cSSLContext, rb_intern("verify_callback"), 1, 1, Qfalse); + + /* + * Sets various OpenSSL options. + */ + rb_attr(cSSLContext, rb_intern("options"), 1, 1, Qfalse); + + /* + * An OpenSSL::X509::Store used for certificate verification + */ + rb_attr(cSSLContext, rb_intern("cert_store"), 1, 1, Qfalse); + + /* + * An Array of extra X509 certificates to be added to the certificate + * chain. + */ + rb_attr(cSSLContext, rb_intern("extra_chain_cert"), 1, 1, Qfalse); + + /* + * A callback invoked when a client certificate is requested by a server + * and no certificate has been set. + * + * The callback is invoked with a Session and must return an Array + * containing an OpenSSL::X509::Certificate and an OpenSSL::PKey. If any + * other value is returned the handshake is suspended. + */ + rb_attr(cSSLContext, rb_intern("client_cert_cb"), 1, 1, Qfalse); + + /* + * A callback invoked when DH parameters are required. + * + * The callback is invoked with the Session for the key exchange, an + * flag indicating the use of an export cipher and the keylength + * required. + * + * The callback must return an OpenSSL::PKey::DH instance of the correct + * key length. + */ + rb_attr(cSSLContext, rb_intern("tmp_dh_callback"), 1, 1, Qfalse); + + /* + * Sets the context in which a session can be reused. This allows + * sessions for multiple applications to be distinguished, for exapmle, by + * name. + */ + rb_attr(cSSLContext, rb_intern("session_id_context"), 1, 1, Qfalse); + + /* + * A callback invoked on a server when a session is proposed by the client + * but the session could not be found in the server's internal cache. + * + * The callback is invoked with the SSLSocket and session id. The + * callback may return a Session from an external cache. + */ + rb_attr(cSSLContext, rb_intern("session_get_cb"), 1, 1, Qfalse); + + /* + * A callback invoked when a new session was negotiatied. + * + * The callback is invoked with an SSLSocket. If false is returned the + * session will be removed from the internal cache. + */ + rb_attr(cSSLContext, rb_intern("session_new_cb"), 1, 1, Qfalse); + + /* + * A callback invoked when a session is removed from the internal cache. + * + * The callback is invoked with an SSLContext and a Session. + */ + rb_attr(cSSLContext, rb_intern("session_remove_cb"), 1, 1, Qfalse); + +#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME + /* + * A callback invoked at connect time to distinguish between multiple + * server names. + * + * The callback is invoked with an SSLSocket and a server name. The + * callback must return an SSLContext for the server name or nil. + */ + rb_attr(cSSLContext, rb_intern("servername_cb"), 1, 1, Qfalse); +#endif + rb_define_alias(cSSLContext, "ssl_timeout", "timeout"); rb_define_alias(cSSLContext, "ssl_timeout=", "timeout="); rb_define_method(cSSLContext, "initialize", ossl_sslctx_initialize, -1); @@ -1599,14 +1812,53 @@ Init_ossl_ssl() rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0); + /* + * No session caching for client or server + */ rb_define_const(cSSLContext, "SESSION_CACHE_OFF", LONG2FIX(SSL_SESS_CACHE_OFF)); + + /* + * Client sessions are added to the session cache + */ rb_define_const(cSSLContext, "SESSION_CACHE_CLIENT", LONG2FIX(SSL_SESS_CACHE_CLIENT)); /* doesn't actually do anything in 0.9.8e */ + + /* + * Server sessions are added to the session cache + */ rb_define_const(cSSLContext, "SESSION_CACHE_SERVER", LONG2FIX(SSL_SESS_CACHE_SERVER)); + + /* + * Both client and server sessions are added to the session cache + */ rb_define_const(cSSLContext, "SESSION_CACHE_BOTH", LONG2FIX(SSL_SESS_CACHE_BOTH)); /* no different than CACHE_SERVER in 0.9.8e */ + + /* + * Normally the sesison cache is checked for expired sessions every 255 + * connections. Since this may lead to a delay that cannot be controlled, + * the automatic flushing may be disabled and #flush_sessions can be + * called explicitly. + */ rb_define_const(cSSLContext, "SESSION_CACHE_NO_AUTO_CLEAR", LONG2FIX(SSL_SESS_CACHE_NO_AUTO_CLEAR)); + + /* + * Always perform external lookups of sessions even if they are in the + * internal cache. + * + * This flag has no effect on clients + */ rb_define_const(cSSLContext, "SESSION_CACHE_NO_INTERNAL_LOOKUP", LONG2FIX(SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)); + + /* + * Never automatically store sessions in the internal store. + */ rb_define_const(cSSLContext, "SESSION_CACHE_NO_INTERNAL_STORE", LONG2FIX(SSL_SESS_CACHE_NO_INTERNAL_STORE)); + + /* + * Enables both SESSION_CACHE_NO_INTERNAL_LOOKUP and + * SESSION_CACHE_NO_INTERNAL_STORE. + */ rb_define_const(cSSLContext, "SESSION_CACHE_NO_INTERNAL", LONG2FIX(SSL_SESS_CACHE_NO_INTERNAL)); + rb_define_method(cSSLContext, "session_add", ossl_sslctx_session_add, 1); rb_define_method(cSSLContext, "session_remove", ossl_sslctx_session_remove, 1); rb_define_method(cSSLContext, "session_cache_mode", ossl_sslctx_get_session_cache_mode, 0); @@ -1621,10 +1873,11 @@ Init_ossl_ssl() rb_ary_push(ary, ID2SYM(rb_intern(ossl_ssl_method_tab[i].name))); } rb_obj_freeze(ary); - /* holds a list of available SSL/TLS methods */ + /* The list of available SSL/TLS methods */ rb_define_const(cSSLContext, "METHODS", ary); - /* class SSLSocket + /* + * Document-class: OpenSSL::SSL::SSLSocket * * The following attributes are available but don't show up in rdoc. * * io, context, sync_close diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c index 0d4f39cb02..48b3927f8f 100644 --- a/ext/openssl/ossl_ssl_session.c +++ b/ext/openssl/ossl_ssl_session.c @@ -278,8 +278,8 @@ static VALUE ossl_ssl_session_to_text(VALUE self) void Init_ossl_ssl_session(void) { -#if 0 /* let rdoc know about mOSSL */ - mOSSL = rb_define_module("OpenSSL"); +#if 0 + mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ mSSL = rb_define_module_under(mOSSL, "SSL"); #endif cSSLSession = rb_define_class_under(mSSL, "Session", rb_cObject); -- cgit v1.2.3