aboutsummaryrefslogtreecommitdiffstats
path: root/test/monniaux/BearSSL/samples/server_basic.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/monniaux/BearSSL/samples/server_basic.c')
-rw-r--r--test/monniaux/BearSSL/samples/server_basic.c436
1 files changed, 436 insertions, 0 deletions
diff --git a/test/monniaux/BearSSL/samples/server_basic.c b/test/monniaux/BearSSL/samples/server_basic.c
new file mode 100644
index 00000000..e774c4d2
--- /dev/null
+++ b/test/monniaux/BearSSL/samples/server_basic.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+
+#include "bearssl.h"
+
+/*
+ * This sample code can use three possible certificate chains:
+ * -- A full-RSA chain (server key is RSA, certificates are signed with RSA)
+ * -- A full-EC chain (server key is EC, certificates are signed with ECDSA)
+ * -- A mixed chain (server key is EC, certificates are signed with RSA)
+ *
+ * The macros below define which chain is selected. This impacts the list
+ * of supported cipher suites.
+ *
+ * Other macros, which can be defined (with a non-zero value):
+ *
+ * SERVER_PROFILE_MIN_FS
+ * Select a "minimal" profile with forward security (ECDHE cipher
+ * suite).
+ *
+ * SERVER_PROFILE_MIN_NOFS
+ * Select a "minimal" profile without forward security (RSA or ECDH
+ * cipher suite, but not ECDHE).
+ *
+ * SERVER_CHACHA20
+ * If SERVER_PROFILE_MIN_FS is selected, then this macro selects
+ * a cipher suite with ChaCha20+Poly1305; otherwise, AES/GCM is
+ * used. This macro has no effect otherwise, since there is no
+ * non-forward secure cipher suite that uses ChaCha20+Poly1305.
+ */
+
+#if !(SERVER_RSA || SERVER_EC || SERVER_MIXED)
+#define SERVER_RSA 1
+#define SERVER_EC 0
+#define SERVER_MIXED 0
+#endif
+
+#if SERVER_RSA
+#include "chain-rsa.h"
+#include "key-rsa.h"
+#define SKEY RSA
+#elif SERVER_EC
+#include "chain-ec.h"
+#include "key-ec.h"
+#define SKEY EC
+#elif SERVER_MIXED
+#include "chain-ec+rsa.h"
+#include "key-ec.h"
+#define SKEY EC
+#else
+#error Must use one of RSA, EC or MIXED chains.
+#endif
+
+/*
+ * Create a server socket bound to the specified host and port. If 'host'
+ * is NULL, this will bind "generically" (all addresses).
+ *
+ * Returned value is the server socket descriptor, or -1 on error.
+ */
+static int
+host_bind(const char *host, const char *port)
+{
+ struct addrinfo hints, *si, *p;
+ int fd;
+ int err;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo(host, port, &hints, &si);
+ if (err != 0) {
+ fprintf(stderr, "ERROR: getaddrinfo(): %s\n",
+ gai_strerror(err));
+ return -1;
+ }
+ fd = -1;
+ for (p = si; p != NULL; p = p->ai_next) {
+ struct sockaddr *sa;
+ struct sockaddr_in sa4;
+ struct sockaddr_in6 sa6;
+ size_t sa_len;
+ void *addr;
+ char tmp[INET6_ADDRSTRLEN + 50];
+ int opt;
+
+ sa = (struct sockaddr *)p->ai_addr;
+ if (sa->sa_family == AF_INET) {
+ sa4 = *(struct sockaddr_in *)sa;
+ sa = (struct sockaddr *)&sa4;
+ sa_len = sizeof sa4;
+ addr = &sa4.sin_addr;
+ if (host == NULL) {
+ sa4.sin_addr.s_addr = INADDR_ANY;
+ }
+ } else if (sa->sa_family == AF_INET6) {
+ sa6 = *(struct sockaddr_in6 *)sa;
+ sa = (struct sockaddr *)&sa6;
+ sa_len = sizeof sa6;
+ addr = &sa6.sin6_addr;
+ if (host == NULL) {
+ sa6.sin6_addr = in6addr_any;
+ }
+ } else {
+ addr = NULL;
+ sa_len = p->ai_addrlen;
+ }
+ if (addr != NULL) {
+ inet_ntop(p->ai_family, addr, tmp, sizeof tmp);
+ } else {
+ sprintf(tmp, "<unknown family: %d>",
+ (int)sa->sa_family);
+ }
+ fprintf(stderr, "binding to: %s\n", tmp);
+ fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+ if (fd < 0) {
+ perror("socket()");
+ continue;
+ }
+ opt = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
+ opt = 0;
+ setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof opt);
+ if (bind(fd, sa, sa_len) < 0) {
+ perror("bind()");
+ close(fd);
+ continue;
+ }
+ break;
+ }
+ if (p == NULL) {
+ freeaddrinfo(si);
+ fprintf(stderr, "ERROR: failed to bind\n");
+ return -1;
+ }
+ freeaddrinfo(si);
+ if (listen(fd, 5) < 0) {
+ perror("listen()");
+ close(fd);
+ return -1;
+ }
+ fprintf(stderr, "bound.\n");
+ return fd;
+}
+
+/*
+ * Accept a single client on the provided server socket. This is blocking.
+ * On error, this returns -1.
+ */
+static int
+accept_client(int server_fd)
+{
+ int fd;
+ struct sockaddr sa;
+ socklen_t sa_len;
+ char tmp[INET6_ADDRSTRLEN + 50];
+ const char *name;
+
+ sa_len = sizeof sa;
+ fd = accept(server_fd, &sa, &sa_len);
+ if (fd < 0) {
+ perror("accept()");
+ return -1;
+ }
+ name = NULL;
+ switch (sa.sa_family) {
+ case AF_INET:
+ name = inet_ntop(AF_INET,
+ &((struct sockaddr_in *)&sa)->sin_addr,
+ tmp, sizeof tmp);
+ break;
+ case AF_INET6:
+ name = inet_ntop(AF_INET6,
+ &((struct sockaddr_in6 *)&sa)->sin6_addr,
+ tmp, sizeof tmp);
+ break;
+ }
+ if (name == NULL) {
+ sprintf(tmp, "<unknown: %lu>", (unsigned long)sa.sa_family);
+ name = tmp;
+ }
+ fprintf(stderr, "accepting connection from: %s\n", name);
+ return fd;
+}
+
+/*
+ * Low-level data read callback for the simplified SSL I/O API.
+ */
+static int
+sock_read(void *ctx, unsigned char *buf, size_t len)
+{
+ for (;;) {
+ ssize_t rlen;
+
+ rlen = read(*(int *)ctx, buf, len);
+ if (rlen <= 0) {
+ if (rlen < 0 && errno == EINTR) {
+ continue;
+ }
+ return -1;
+ }
+ return (int)rlen;
+ }
+}
+
+/*
+ * Low-level data write callback for the simplified SSL I/O API.
+ */
+static int
+sock_write(void *ctx, const unsigned char *buf, size_t len)
+{
+ for (;;) {
+ ssize_t wlen;
+
+ wlen = write(*(int *)ctx, buf, len);
+ if (wlen <= 0) {
+ if (wlen < 0 && errno == EINTR) {
+ continue;
+ }
+ return -1;
+ }
+ return (int)wlen;
+ }
+}
+
+/*
+ * Sample HTTP response to send.
+ */
+static const char *HTTP_RES =
+ "HTTP/1.0 200 OK\r\n"
+ "Content-Length: 46\r\n"
+ "Connection: close\r\n"
+ "Content-Type: text/html; charset=iso-8859-1\r\n"
+ "\r\n"
+ "<html>\r\n"
+ "<body>\r\n"
+ "<p>Test!</p>\r\n"
+ "</body>\r\n"
+ "</html>\r\n";
+
+/*
+ * Main program: this is a simple program that expects 1 argument: a
+ * port number. This will start a simple network server on that port,
+ * that expects incoming SSL clients. It handles only one client at a
+ * time (handling several would require threads, sub-processes, or
+ * multiplexing with select()/poll(), all of which being possible).
+ *
+ * For each client, the server will wait for two successive newline
+ * characters (ignoring CR characters, so CR+LF is accepted), then
+ * produce a sample static HTTP response. This is very crude, but
+ * sufficient for explanatory purposes.
+ */
+int
+main(int argc, char *argv[])
+{
+ const char *port;
+ int fd;
+
+ if (argc != 2) {
+ return EXIT_FAILURE;
+ }
+ port = argv[1];
+
+ /*
+ * Ignore SIGPIPE to avoid crashing in case of abrupt socket close.
+ */
+ signal(SIGPIPE, SIG_IGN);
+
+ /*
+ * Open the server socket.
+ */
+ fd = host_bind(NULL, port);
+ if (fd < 0) {
+ return EXIT_FAILURE;
+ }
+
+ /*
+ * Process each client, one at a time.
+ */
+ for (;;) {
+ int cfd;
+ br_ssl_server_context sc;
+ unsigned char iobuf[BR_SSL_BUFSIZE_BIDI];
+ br_sslio_context ioc;
+ int lcwn, err;
+
+ cfd = accept_client(fd);
+ if (cfd < 0) {
+ return EXIT_FAILURE;
+ }
+
+ /*
+ * Initialise the context with the cipher suites and
+ * algorithms. This depends on the server key type
+ * (and, for EC keys, the signature algorithm used by
+ * the CA to sign the server's certificate).
+ *
+ * Depending on the defined macros, we may select one of
+ * the "minimal" profiles. Key exchange algorithm depends
+ * on the key type:
+ * RSA key: RSA or ECDHE_RSA
+ * EC key, cert signed with ECDSA: ECDH_ECDSA or ECDHE_ECDSA
+ * EC key, cert signed with RSA: ECDH_RSA or ECDHE_ECDSA
+ */
+#if SERVER_RSA
+#if SERVER_PROFILE_MIN_FS
+#if SERVER_CHACHA20
+ br_ssl_server_init_mine2c(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#else
+ br_ssl_server_init_mine2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#endif
+#elif SERVER_PROFILE_MIN_NOFS
+ br_ssl_server_init_minr2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#else
+ br_ssl_server_init_full_rsa(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#endif
+#elif SERVER_EC
+#if SERVER_PROFILE_MIN_FS
+#if SERVER_CHACHA20
+ br_ssl_server_init_minf2c(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#else
+ br_ssl_server_init_minf2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#endif
+#elif SERVER_PROFILE_MIN_NOFS
+ br_ssl_server_init_minv2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#else
+ br_ssl_server_init_full_ec(&sc, CHAIN, CHAIN_LEN,
+ BR_KEYTYPE_EC, &SKEY);
+#endif
+#else /* SERVER_MIXED */
+#if SERVER_PROFILE_MIN_FS
+#if SERVER_CHACHA20
+ br_ssl_server_init_minf2c(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#else
+ br_ssl_server_init_minf2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#endif
+#elif SERVER_PROFILE_MIN_NOFS
+ br_ssl_server_init_minu2g(&sc, CHAIN, CHAIN_LEN, &SKEY);
+#else
+ br_ssl_server_init_full_ec(&sc, CHAIN, CHAIN_LEN,
+ BR_KEYTYPE_RSA, &SKEY);
+#endif
+#endif
+ /*
+ * Set the I/O buffer to the provided array. We
+ * allocated a buffer large enough for full-duplex
+ * behaviour with all allowed sizes of SSL records,
+ * hence we set the last argument to 1 (which means
+ * "split the buffer into separate input and output
+ * areas").
+ */
+ br_ssl_engine_set_buffer(&sc.eng, iobuf, sizeof iobuf, 1);
+
+ /*
+ * Reset the server context, for a new handshake.
+ */
+ br_ssl_server_reset(&sc);
+
+ /*
+ * Initialise the simplified I/O wrapper context.
+ */
+ br_sslio_init(&ioc, &sc.eng, sock_read, &cfd, sock_write, &cfd);
+
+ /*
+ * Read bytes until two successive LF (or CR+LF) are received.
+ */
+ lcwn = 0;
+ for (;;) {
+ unsigned char x;
+
+ if (br_sslio_read(&ioc, &x, 1) < 0) {
+ goto client_drop;
+ }
+ if (x == 0x0D) {
+ continue;
+ }
+ if (x == 0x0A) {
+ if (lcwn) {
+ break;
+ }
+ lcwn = 1;
+ } else {
+ lcwn = 0;
+ }
+ }
+
+ /*
+ * Write a response and close the connection.
+ */
+ br_sslio_write_all(&ioc, HTTP_RES, strlen(HTTP_RES));
+ br_sslio_close(&ioc);
+
+ client_drop:
+ err = br_ssl_engine_last_error(&sc.eng);
+ if (err == 0) {
+ fprintf(stderr, "SSL closed (correctly).\n");
+ } else {
+ fprintf(stderr, "SSL error: %d\n", err);
+ }
+ close(cfd);
+ }
+}