-
-
Save void52/33d0c357f5023fd6c61b81c3524cecd7 to your computer and use it in GitHub Desktop.
Revisions
-
darrenjs revised this gist
Mar 22, 2017 . 1 changed file with 7 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,11 @@ /* This file had now been added to the git repo https://github.com/darrenjs/openssl_examples ... which also includes a non blocking client example. ------------------------------------------------------------------------------- ssl_server_nonblock.c -- Copyright 2017 Darren Smith -- MIT license -
darrenjs revised this gist
Mar 21, 2017 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -502,4 +502,4 @@ int main(int argc, char **argv) } return 0; } -
darrenjs revised this gist
Mar 21, 2017 . 1 changed file with 32 additions and 37 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -39,7 +39,7 @@ Compilation To compile the program, use something like: gcc -Wall -O0 -g3 -std=c99 -lcrypto -lssl -o ssl_server_nonblock ssl_server_nonblock.c Running ------- @@ -65,21 +65,21 @@ these steps: rm -f server.csr The openssl program can also be used to connect to this program as an SSL client. Here's an example command (assuming we're using port 55555): openssl s_client -connect 127.0.0.1:55555 -msg -debug -state -showcerts Flow of encrypted & unencrypted bytes ------------------------------------- This diagram shows how the read and write memory BIO's (rbio & wbio) are associated with the socket read and write respectively. On the inbound flow (data into the program) bytes are read from the socket and copied into the rbio via BIO_write. This represents the the transfer of encrypted data into the SSL object. The unencrypted data is then obtained through calling SSL_read. The reverse happens on the outbound flow to convey unencrypted user data into a socket write of encrypted data. +------+ +-----+ @@ -102,11 +102,8 @@ encrypted data. #include <openssl/ssl.h> #include <arpa/inet.h> #include <poll.h> #include <stdio.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> @@ -117,7 +114,7 @@ encrypted data. void handle_error(const char *file, int lineno, const char *msg) { fprintf(stderr, "** %s:%i %s\n", file, lineno, msg); ERR_print_errors_fp(stderr); exit(1); } #define int_error(msg) handle_error(__FILE__, __LINE__, msg) @@ -152,7 +149,7 @@ struct ssl_client char* write_buf; size_t write_len; /* Bytes waiting to be fed into the SSL object for encryption. */ char* encrypt_buf; size_t encrypt_len; @@ -216,8 +213,8 @@ void send_unencrypted_bytes(const char *buf, size_t len) client.encrypt_len += len; } /* Queue encrypted bytes for socket write. Should only be used when the SSL * object has requested a write operation. */ void queue_encrypted_bytes(const char *buf, size_t len) { client.write_buf = (char*)realloc(client.write_buf, client.write_len + len); @@ -226,24 +223,22 @@ void queue_encrypted_bytes(const char *buf, size_t len) } /* Process SSL bytes received from the peer. The data needs to be fed into the SSL object to be unencrypted. On success returns 0, on SSL error -1. */ int on_read_cb(char* src, size_t len) { char buf[DEFAULT_BUF_SIZE]; /* used for copying bytes out of SSL/BIO */ enum sslstatus status; int n; while (len > 0) { n = BIO_write(client.rbio, src, len); if (n<=0) return -1; /* if BIO write fails, assume unrecoverable */ src += n; len -= n; if (!SSL_is_init_finished(client.ssl)) { n = SSL_accept(client.ssl); status = get_sslstatus(client.ssl, n); @@ -294,7 +289,7 @@ int on_read_cb(char* src, size_t len) return 0; } /* Process outbound unencrypted data that are waiting to be encrypted. The * waiting data resides in encrypt_buf. It needs to be passed into the SSL * object for encryption, which in turn generates the encrypted bytes that then * will be queued for later socket write. */ @@ -357,36 +352,36 @@ int do_sock_read() } /* Write encrypted bytes to the socket. */ int do_sock_write() { ssize_t n = write(client.fd, client.write_buf, client.write_len); if (n>0) { if ((size_t)n<client.write_len) memmove(client.write_buf, client.write_buf+n, client.write_len-n); client.write_len -= n; client.write_buf = (char*)realloc(client.write_buf, client.write_len); return 0; } else return -1; } void ssl_init() { printf("initialising SSL\n"); /* SSL library initialisation */ SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); ERR_load_BIO_strings(); ERR_load_crypto_strings(); /* create the SSL server context */ ctx = SSL_CTX_new(SSLv23_server_method()); if (!ctx) die("SSL_CTX_new()"); /* Load certificate and private key files, and check consistency */ int err; err = SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM); if (err != 1) @@ -402,7 +397,6 @@ void ssl_init() { printf("private-key file loaded ok\n"); /* Make sure the key and certificate file match. */ if (SSL_CTX_check_private_key(ctx) != 1) int_error("SSL_CTX_check_private_key failed"); else @@ -412,13 +406,10 @@ void ssl_init() { SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); } int main(int argc, char **argv) { char str[INET_ADDRSTRLEN]; int port = (argc>1)? atoi(argv[1]):55555; int servfd = socket(AF_INET, SOCK_STREAM, 0); if (servfd < 0) @@ -451,6 +442,8 @@ int main(int argc, char **argv) fdset[0].fd = STDIN_FILENO; fdset[0].events = POLLIN; ssl_init(); while (1) { printf("waiting for next connection on port %d\n", port); @@ -468,14 +461,15 @@ int main(int argc, char **argv) /* event loop */ fdset[1].events = POLLERR | POLLHUP | POLLNVAL | POLLIN; #ifdef POLLRDHUP fdset[1].events |= POLLRDHUP; #endif while (1) { fdset[1].events &= ~POLLOUT; fdset[1].events |= (ssl_client_want_write(&client)? POLLOUT : 0); int nready = poll(&fdset[0], 2, -1); if (nready == 0) @@ -486,7 +480,8 @@ int main(int argc, char **argv) if (do_sock_read() == -1) break; if (revents & POLLOUT) if (do_sock_write() == -1) break; if (revents & (POLLERR | POLLHUP | POLLNVAL)) break; -
darrenjs revised this gist
Mar 20, 2017 . 1 changed file with 103 additions and 73 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -20,31 +20,42 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- ssl_server_nonblock.c is a simple OpenSSL example program to illustrate the use of memory BIO's (BIO_s_mem) to perform SSL read and write with non-blocking socket IO. The program accepts connections from SSL clients. To keep it simple only a single live connection is supported. While a client is connected the program will receive any bytes which it sends, unencrypt them and write to stdout, using non-blocking socket reads. It will also read from stdin, encrypt the bytes and send to the client, using non-blocking socket writes. Note that this program is single threaded. This means it does not have to set up SSL locking. The program does not exit, and so it does not have code to free up the resources associated with the SSL context and library. Compilation ----------- To compile the program, use something like: gcc -Wall -O0 -g3 -std=c99 ssl_server_nonblock.c -lcrypto -lssl -o ssl_server_nonblock Running ------- Running the program requires that a SSL certificate and private key are available to be loaded. These can be generated using the 'openssl' program using these steps: 1. Generate the private key, this is what we normally keep secret: openssl genrsa -des3 -passout pass:x -out server.pass.key 2048 openssl rsa -passin pass:x -in server.pass.key -out server.key rm -f server.pass.key 2. Next generate the CSR. We can leave the password empty when prompted (because this is self-sign): openssl req -new -key server.key -out server.csr @@ -53,14 +64,35 @@ available to be loaded. These can generated using the openssl program. openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt rm -f server.csr The openssl program can also be used to connect to this program as an SSL client. Here's example command (assuming we're using port 55555): openssl s_client -connect 127.0.0.1:55555 -msg -debug -state -showcerts Flow of encrypted & unencrypted bytes ------------------------------------- This diagram shows how the rbio & wbio memory BIO's are associated with the socket read and write respectively. On the inbound flow (data into the program) bytes are read from the socket and copied into the rbio via BIO_write. This represents the the transfer of encrypted data into the SSL object. The unencrypted data is then obtained through calling SSL_read. The reverse happens on the outbound flow to convey unencrypted user data into a socket write of encrypted data. +------+ +-----+ |......|--> read(fd) --> BIO_write(rbio) -->|.....|--> SSL_read(ssl) --> IN |......| |.....| |.sock.| |.SSL.| |......| |.....| |......|<-- write(fd) <-- BIO_read(wbio) <--|.....|<-- SSL_write(ssl) <-- OUT +------+ +-----+ | | | | |<-------------------------------->| |<------------------->| | encrypted bytes | | unencrypted bytes | */ @@ -80,11 +112,14 @@ To compile the program, use something like: #include <sys/types.h> #include <unistd.h> #define DEFAULT_BUF_SIZE 64 void handle_error(const char *file, int lineno, const char *msg) { fprintf(stderr, "** %s:%i %s\n", file, lineno, msg); ERR_print_errors_fp(stderr); exit(-1); } #define int_error(msg) handle_error(__FILE__, __LINE__, msg) void die(const char *msg) { @@ -125,7 +160,6 @@ struct ssl_client void (*io_on_read)(char *buf, size_t len); } client; void ssl_client_init(struct ssl_client *p) { memset(p, 0, sizeof(struct ssl_client)); @@ -141,9 +175,9 @@ void ssl_client_init(struct ssl_client *p) p->io_on_read = print_unencrypted_data; } void ssl_client_cleanup(struct ssl_client *p) { SSL_free(p->ssl); /* free the SSL object and its BIO's */ free(p->write_buf); free(p->encrypt_buf); } @@ -152,13 +186,14 @@ int ssl_client_want_write(struct ssl_client *cp) { return (cp->write_len>0); } /* Obtain the return value of an SSL operation and convert into a simplified * error code, which is easier to examine for failure. */ enum sslstatus { SSLSTATUS_OK, SSLSTATUS_WANT_IO, SSLSTATUS_FAIL}; static enum sslstatus get_sslstatus(SSL* ssl, int n) { switch (SSL_get_error(ssl, n)) { case SSL_ERROR_NONE: return SSLSTATUS_OK; case SSL_ERROR_WANT_WRITE: @@ -176,44 +211,36 @@ static enum sslstatus get_sslstatus(SSL* ssl, int n) * object. */ void send_unencrypted_bytes(const char *buf, size_t len) { client.encrypt_buf = (char*)realloc(client.encrypt_buf, client.encrypt_len + len); memcpy(client.encrypt_buf+client.encrypt_len, buf, len); client.encrypt_len += len; } /* Queue encrypted bytes. Should only be used when the SSL object has requested a * write operation. */ void queue_encrypted_bytes(const char *buf, size_t len) { client.write_buf = (char*)realloc(client.write_buf, client.write_len + len); memcpy(client.write_buf+client.write_len, buf, len); client.write_len += len; } /* Process SSL bytes received from the peer. The data needs to be fed into the SSL object to be unencrypted. On success, returns 0, on SSL error -1. */ int on_read_cb(char* src, size_t len) { char buf[DEFAULT_BUF_SIZE]; enum sslstatus status; int n; while (len > 0) { n = BIO_write(client.rbio, src, len); if (n<=0) return -1; src += n; len -= n; printf("BIO_write: (n=%d)\n", n); @@ -222,13 +249,14 @@ int on_read_cb(char* buf, size_t nread) status = get_sslstatus(client.ssl, n); /* Did SSL request to write bytes? */ if (status == SSLSTATUS_WANT_IO) do { n = BIO_read(client.wbio, buf, sizeof(buf)); if (n > 0) queue_encrypted_bytes(buf, n); else if (!BIO_should_retry(client.wbio)) return -1; } while (n>0); if (status == SSLSTATUS_FAIL) return -1; @@ -241,22 +269,23 @@ int on_read_cb(char* buf, size_t nread) * read of unencrypted data. */ do { n = SSL_read(client.ssl, buf, sizeof(buf)); if (n > 0) client.io_on_read(buf, (size_t)n); } while (n > 0); status = get_sslstatus(client.ssl, n); /* Did SSL request to write bytes? This can happen if peer has requested SSL * renegotiation. */ if (status == SSLSTATUS_WANT_IO) do { n = BIO_read(client.wbio, buf, sizeof(buf)); if (n > 0) queue_encrypted_bytes(buf, n); else if (!BIO_should_retry(client.wbio)) return -1; } while (n>0); if (status == SSLSTATUS_FAIL) return -1; @@ -271,29 +300,30 @@ int on_read_cb(char* buf, size_t nread) * will be queued for later socket write. */ int do_encrypt() { char buf[DEFAULT_BUF_SIZE]; enum sslstatus status; if (!SSL_is_init_finished(client.ssl)) return 0; while (client.encrypt_len>0) { int n = SSL_write(client.ssl, client.encrypt_buf, client.encrypt_len); status = get_sslstatus(client.ssl, n); if (n>0) { /* consume the waiting bytes that have been used by SSL */ if ((size_t)n<client.encrypt_len) memmove(client.encrypt_buf, client.encrypt_buf+n, client.encrypt_len-n); client.encrypt_len -= n; client.encrypt_buf = (char*)realloc(client.encrypt_buf, client.encrypt_len); /* take the output of the SSL object and queue it for socket write */ do { n = BIO_read(client.wbio, buf, sizeof(buf)); if (n > 0) queue_encrypted_bytes(buf, n); else if (!BIO_should_retry(client.wbio)) return -1; } while (n>0); } @@ -309,7 +339,7 @@ int do_encrypt() /* Read bytes from stdin and queue for later encryption. */ void do_stdin_read() { char buf[DEFAULT_BUF_SIZE]; ssize_t n = read(STDIN_FILENO, buf, sizeof(buf)); if (n>0) send_unencrypted_bytes(buf, (size_t)n); @@ -318,7 +348,7 @@ void do_stdin_read() /* Read encrypted bytes from socket. */ int do_sock_read() { char buf[DEFAULT_BUF_SIZE]; ssize_t n = read(client.fd, buf, sizeof(buf)); if (n>0) return on_read_cb(buf, (size_t)n); @@ -330,12 +360,11 @@ int do_sock_read() void do_sock_write() { ssize_t n = write(client.fd, client.write_buf, client.write_len); if (n>0) { if ((size_t)n<client.write_len) memmove(client.write_buf, client.write_buf+n, client.write_len-n); client.write_len -= n; client.write_buf = (char*)realloc(client.write_buf, client.write_len); } } @@ -350,11 +379,9 @@ void ssl_init() { ERR_load_BIO_strings(); ERR_load_crypto_strings(); /* create the SSL server context */ ctx = SSL_CTX_new(SSLv23_server_method()); if (!ctx) die("SSL_CTX_new()"); @@ -380,6 +407,9 @@ void ssl_init() { int_error("SSL_CTX_check_private_key failed"); else printf("private key verified ok\n"); /* Recommended to avoid SSLv2 & SSLv3 */ SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); } @@ -388,7 +418,7 @@ int main(int argc, char **argv) ssl_init(); char str[INET_ADDRSTRLEN]; int port = 55555; int servfd = socket(AF_INET, SOCK_STREAM, 0); if (servfd < 0) @@ -446,7 +476,7 @@ int main(int argc, char **argv) fdset[1].events |= POLLRDHUP; #endif int nready = poll(&fdset[0], 2, -1); if (nready == 0) continue; /* no fd ready */ @@ -473,8 +503,8 @@ int main(int argc, char **argv) } close(fdset[1].fd); ssl_client_cleanup(&client); } return 0; } -
darrenjs created this gist
Mar 19, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,480 @@ /* ------------------------------------------------------------------------------- ssl_server_nonblock.c -- Copyright 2017 Darren Smith -- MIT license Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- ssl_server_nonblock.c is a simple example to illustrate the use of memory BIO's (BIO_s_mem) to perform SSL read and write with non-blocking socket IO, based on a poll event loop. The program accepts connections from SSL clients. To keep it simple only a single live connection is supported. While a client is connected, the program will receive bytes from the client, unencrypt them and write to stdout, using non-blocking socket reads. It will also read from stdin, encrypt the bytes and send to the client, using non-blocking socket writes. Note that this program is single threaded, which means we don't have to set up any SSL locks. The program does not exit, thus is does not have any code to free up the resources associated with the SSL context and library. Running the program requires that an SSL certificate and private key are available to be loaded. These can generated using the openssl program. 1. Generate the private key, this is what we normally keep secret: openssl genrsa -des3 -passout pass:x -out server.pass.key 2048 openssl rsa -passin pass:x -in server.pass.key -out server.key rm -f server.pass.key 2. Next generate the CSR. We can leave the password empty, when prompted (because this is self-sign). openssl req -new -key server.key -out server.csr 3. Next generate the self signed certificate: openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt rm -f server.csr The openssl progam can also be used to connect to this program as an SSL client. Here's example command (assuming we're using port 55555) openssl s_client -connect 127.0.0.1:55555 -msg -debug -state -showcerts To compile the program, use something like: gcc -Wall -O0 -g3 -std=c99 ssl_server_nonblock.c -lcrypto -lssl -o ssl_server_nonblock.c */ #include <openssl/bio.h> #include <openssl/err.h> #include <openssl/pem.h> #include <openssl/ssl.h> #include <arpa/inet.h> #include <errno.h> #include <poll.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> void handle_error(const char *file, int lineno, const char *msg) { fprintf(stderr, "** %s:%i %s\n", file, lineno, msg); ERR_print_errors_fp(stderr); exit(-1); } #define int_error(msg) handle_error(__FILE__, __LINE__, msg) void die(const char *msg) { perror(msg); exit(1); } void print_unencrypted_data(char *buf, size_t len) { printf("%.*s", (int)len, buf); } /* Global SSL context */ SSL_CTX *ctx; /* An instance of this object is created each time a client connection is * accepted. It stores the client file descriptor, the SSL objects, and data * which is waiting to be either written to socket or encrypted. */ struct ssl_client { int fd; SSL *ssl; BIO *rbio; /* SSL reads from, we write to. */ BIO *wbio; /* SSL writes to, we read from. */ /* Bytes waiting to be written to socket. This is data that has been generated * by the SSL object, either due to encryption of user input, or, writes * requires due to peer-requested SSL renegotiation. */ char* write_buf; size_t write_len; /* Bytes waiting to be encrypted by the SSL object. */ char* encrypt_buf; size_t encrypt_len; /* Method to invoke when unencrypted bytes are available. */ void (*io_on_read)(char *buf, size_t len); } client; void ssl_client_init(struct ssl_client *p) { memset(p, 0, sizeof(struct ssl_client)); p->rbio = BIO_new(BIO_s_mem()); p->wbio = BIO_new(BIO_s_mem()); p->ssl = SSL_new(ctx); SSL_set_accept_state(p->ssl); /* sets ssl to work in server mode. */ SSL_set_bio(p->ssl, p->rbio, p->wbio); p->io_on_read = print_unencrypted_data; } void ssl_client_free(struct ssl_client *p) { SSL_free(p->ssl); /* free the SSL object and its BIOs */ free(p->write_buf); free(p->encrypt_buf); } int ssl_client_want_write(struct ssl_client *cp) { return (cp->write_len>0); } /* Take the return value of an SSL/BIO operation and return a simplified error * code, which is easier to examine for failure. */ enum sslstatus { SSLSTATUS_OK, SSLSTATUS_WANT_IO, SSLSTATUS_FAIL}; static enum sslstatus get_sslstatus(SSL* ssl, int n) { switch (SSL_get_error(ssl, n)) { case SSL_ERROR_NONE: return SSLSTATUS_OK; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: return SSLSTATUS_WANT_IO; case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_SYSCALL: default: return SSLSTATUS_FAIL; } } /* Handle request to send unencrypted data to the SSL. All we do here is just * queue the data into the encrypt_buf for later processing by the SSL * object. */ void send_unencrypted_bytes(const char *buf, size_t len) { if (len>0) { client.encrypt_buf = realloc(client.encrypt_buf, client.encrypt_len + len); memcpy(client.encrypt_buf+client.encrypt_len, buf, len); client.encrypt_len += len; } } /* Queue encrypted bytes. Should ony be used when the SSL object has requested a * write operation. */ void queue_encrypted_bytes(const char *buf, size_t len) { if (len>0) { client.write_buf = realloc(client.write_buf, client.write_len + len); memcpy(client.write_buf+client.write_len, buf, len); client.write_len += len; } } /* Process SSL bytes received from the peer. The data needs to be fed into the SSL object for unencryption. On success, returns 0, on SSL error -1. */ int on_read_cb(char* buf, size_t nread) { char readbuf[1024]; char writebuf[1024]; enum sslstatus status; int n; while (nread > 0) { /* TODO: check: can a memory BIO really return 0. do the same in other BIO functions below.*/ n = BIO_write(client.rbio, buf, nread); if (n<=0) return -1; buf += (n > 0) ? n : 0; nread -= (n > 0) ? n : 0; printf("BIO_write: (n=%d)\n", n); if (!SSL_is_init_finished(client.ssl)) { n = SSL_accept(client.ssl); status = get_sslstatus(client.ssl, n); /* Did SSL request to write bytes? */ if (status == SSLSTATUS_WANT_IO) { do { n = BIO_read(client.wbio, writebuf, sizeof(writebuf)); if (n > 0) queue_encrypted_bytes(writebuf, n); } while (n>0); } if (status == SSLSTATUS_FAIL) return -1; if (!SSL_is_init_finished(client.ssl)) return 0; } /* The encrypted data is now in the input bio so now we can perform actual * read of unencrypted data. */ do { n = SSL_read(client.ssl, readbuf, sizeof(readbuf)); if (n > 0) client.io_on_read(readbuf, n); } while (n > 0); status = get_sslstatus(client.ssl, n); /* Did SSL request to write bytes? This can happen if peer has requested SSL * renegotiation. */ if (status == SSLSTATUS_WANT_IO) { do { n = BIO_read(client.wbio, writebuf, sizeof(writebuf)); if (n > 0) queue_encrypted_bytes(writebuf, n); } while (n>0); } if (status == SSLSTATUS_FAIL) return -1; } return 0; } /* Process outbound unencrypted data that is waiting to be encrypted. The * waiting data resides in encrypt_buf. It needs to be passed into the SSL * object for encryption, which in turn generates the encrypted bytes that then * will be queued for later socket write. */ int do_encrypt() { char buf[1024]; enum sslstatus status; if (!SSL_is_init_finished(client.ssl)) return 0; while (client.encrypt_len>0) { int n = SSL_write(client.ssl, client.encrypt_buf, client.encrypt_len); status = get_sslstatus(client.ssl, n); if (n>0) { /* consume the waiting bytes that have just been used by SSL */ if (n<client.encrypt_len) memmove(client.encrypt_buf, client.encrypt_buf+n, client.encrypt_len-n); client.encrypt_len -= n; client.encrypt_buf = realloc(client.encrypt_buf, client.encrypt_len); /* take the output of the SSL object and queue it for socket write */ do { n = BIO_read(client.wbio, buf, sizeof(buf)); if (n > 0) queue_encrypted_bytes(buf, n); } while (n>0); } if (status == SSLSTATUS_FAIL) return -1; if (n==0) break; } return 0; } /* Read bytes from stdin and queue for later encryption. */ void do_stdin_read() { char buf[1024]; ssize_t n = read(STDIN_FILENO, buf, sizeof(buf)); if (n>0) send_unencrypted_bytes(buf, (size_t)n); } /* Read encrypted bytes from socket. */ int do_sock_read() { char buf[1024]; ssize_t n = read(client.fd, buf, sizeof(buf)); if (n>0) return on_read_cb(buf, (size_t)n); else return -1; } /* Write encrypted bytes to the socket. */ void do_sock_write() { ssize_t n = write(client.fd, client.write_buf, client.write_len); if (n>0) { if (n<client.write_len) memmove(client.write_buf, client.write_buf+n, client.write_len-n); client.write_len -= n; client.write_buf = realloc(client.write_buf, client.write_len); } } void ssl_init() { printf("initialising SSL\n"); /* SSL library initialisation */ SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); ERR_load_BIO_strings(); ERR_load_crypto_strings(); /* create a context */ const SSL_METHOD *method = TLSv1_server_method(); /* create SSL server */ ctx = SSL_CTX_new(method); if (!ctx) die("SSL_CTX_new()"); /* Load certificate and private key files, and check consistency */ int err; err = SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM); if (err != 1) int_error("SSL_CTX_use_certificate_file failed"); else printf("certificate file loaded ok\n"); /* Indicate the key file to be used */ err = SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM); if (err != 1) int_error("SSL_CTX_use_PrivateKey_file failed"); else printf("private-key file loaded ok\n"); /* Make sure the key and certificate file match. */ if (SSL_CTX_check_private_key(ctx) != 1) int_error("SSL_CTX_check_private_key failed"); else printf("private key verified ok\n"); } int main(int argc, char **argv) { ssl_init(); char str[INET_ADDRSTRLEN]; int port = 55545; int servfd = socket(AF_INET, SOCK_STREAM, 0); if (servfd < 0) die("socket()"); int enable = 1; if (setsockopt(servfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) die("setsockopt(SO_REUSEADDR)"); /* Specify socket address */ struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(port); if (bind(servfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) die("bind()"); if (listen(servfd, 128) < 0) die("listen()"); int clientfd; struct sockaddr_in peeraddr; socklen_t peeraddr_len = sizeof(peeraddr); struct pollfd fdset[2]; memset(&fdset, 0, sizeof(fdset)); fdset[0].fd = STDIN_FILENO; fdset[0].events = POLLIN; while (1) { printf("waiting for next connection on port %d\n", port); clientfd = accept(servfd, (struct sockaddr *)&peeraddr, &peeraddr_len); if (clientfd < 0) die("accept()"); ssl_client_init(&client); client.fd = clientfd; inet_ntop(peeraddr.sin_family, &peeraddr.sin_addr, str, INET_ADDRSTRLEN); printf("new connection from %s:%d\n", str, ntohs(peeraddr.sin_port)); fdset[1].fd = clientfd; /* event loop */ while (1) { fdset[1].events = (ssl_client_want_write(&client) ? POLLOUT : 0) | POLLERR | POLLHUP | POLLNVAL | POLLIN; #ifdef POLLRDHUP fdset[1].events |= POLLRDHUP; #endif int nready = poll(&fdset[0], 2, -1 /* timeout */); if (nready == 0) continue; /* no fd ready */ int revents = fdset[1].revents; if (revents & POLLIN) if (do_sock_read() == -1) break; if (revents & POLLOUT) do_sock_write(); if (revents & (POLLERR | POLLHUP | POLLNVAL)) break; #ifdef POLLRDHUP if (revents & POLLRDHUP) break; #endif if (fdset[0].revents & POLLIN) do_stdin_read(); if (client.encrypt_len>0) do_encrypt(); } close(fdset[1].fd); ssl_client_free(&client); } return 0; }