Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
9.2.1 Problem
You want to write a network server that can accept SSL connections from clients. 9.2.2 Solution
Creating a server that speaks SSL is not that different from creating a client that speaks SSL (see Recipe 9.1). A small amount of additional setup work is required for servers. In particular, you need to create an spc_x509store_t object (see Recipe 10.5) with a certificate and a private key. The information contained in this object is sent to clients during the initial handshake. In addition, the SPC_X509STORE_USE_CERTIFICATE flag needs to be set in the spc_x509store_t object. With the spc_x509store_t created, calls need to be made to create the listening BIO object, put it into a listening state, and accept new connections. (See Recipe 9.1 for a brief discussion regarding BIO objects.) 9.2.3 Discussion
Once an spc_x509store_t object has been created and fully initialized, the first step in creating an SSL server is to call spc_listen( ). The hostname may be specified as NULL, which indicates that the created socket should be bound to all interfaces. Anything else should be specified in string form as an IP address for the interface to bind to. For example, "127.0.0.1" would cause the server BIO object to bind only to the local loopback interface. #include <stdlib.h> #include <string.h> #include <openssl/bio.h> #include <openssl/ssl.h> BIO *spc_listen(char *host, int port) { BIO *acpt = 0; int addr_length; char *addr; if (port < 1 || port > 65535) return 0; if (!host) host = "*"; addr_length = strlen(host) + 6; /* 5 for int, 1 for colon */ if (!(addr = (char *)malloc(addr_length + 1))) return 0; snprintf(addr, addr_length + 1, "%s:%d", host, port); if ((acpt = BIO_new(BIO_s_accept( ))) != 0) { BIO_set_accept_port(acpt, addr); if (BIO_do_accept(acpt) <= 0) { BIO_free_all(acpt); acpt = 0; } } free(addr); return acpt; } The call to spc_listen( ) will create a BIO object that has an underlying socket that is in a listening state. There isn't actually any SSL work occurring here because an SSL connection will only come into being when a new socket connection is established. The spc_listen( ) call is nonblocking and will return immediately. The next step is to call spc_accept( ) to establish a new socket and possibly an SSL connection between the server and an incoming client. This function should be called repeatedly in order to continually accept connections. However, be aware that it will block if there are no incoming connections pending. The call to spc_accept( ) will either return a new BIO object that is the connection to the new client, or return NULL indicating that there was some failure in establishing the connection.
BIO *spc_accept(BIO *parent, int ssl, spc_x509store_t *spc_store, SSL_CTX **ctx) { BIO *child = 0, *ssl_bio = 0; int our_ctx = 0; SSL *ssl_ptr = 0; if (BIO_do_accept(parent) <= 0) return 0; if (!(child = BIO_pop(parent))) return 0; if (ssl) { if (*ctx) { CRYPTO_add(&((*ctx)->references), 1, CRYPTO_LOCK_SSL_CTX); if (spc_store && spc_store != SSL_CTX_get_app_data(*ctx)) { SSL_CTX_set_cert_store(*ctx, spc_create_x509store(spc_store)); SSL_CTX_set_app_data(*ctx, spc_store); } } else { *ctx = spc_create_sslctx(spc_store); our_ctx = 1; } if (!(ssl_ptr = SSL_new(*ctx))) goto error_exit; SSL_set_bio(ssl_ptr, child, child); if (SSL_accept(ssl_ptr) <= 0) goto error_exit; if (!(ssl_bio = BIO_new(BIO_f_ssl( )))) goto error_exit; BIO_set_ssl(ssl_bio, ssl_ptr, 1); child = ssl_bio; ssl_bio = 0; } return child; error_exit: if (child) BIO_free_all(child); if (ssl_bio) BIO_free_all(ssl_bio); if (ssl_ptr) SSL_free(ssl_ptr); if (*ctx) SSL_CTX_free(*ctx); if (our_ctx) *ctx = 0; return 0; } When a new socket connection is accepted, SSL_accept( ) is called to perform the SSL handshake. The server's certificate (and possibly its chain, depending on how you configure the spc_x509store_t object) is sent to the peer, and if a client certificate is requested and received, it will be verified. If the handshake is successful, the returned BIO object behaves exactly the same as the BIO object that is returned by spc_connect( ) or spc_connect_ssl( ). Regardless of whether a new connection was successfully established, the listening BIO object passed into SSL_accept( ) will be ready for another call to SSL_accept( ) to accept the next connection. 9.2.4 See Also
Recipe 9.1, Recipe 10.5 |